forked from GaijinEntertainment/daScript
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdebug.das
1844 lines (1601 loc) · 74.7 KB
/
debug.das
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
options rtti
options no_aot
options indenting = 4
options no_global_variables = false
options no_unused_block_arguments = false
options no_unused_function_arguments = false
options persistent_heap = true
options strict_smart_pointers = true
module debug shared
require math
require strings
require rtti
require debugapi
require fio
require network
require uriparser
require daslib/json
require daslib/json_boost
require daslib/defer
require daslib/apply_in_context
require daslib/jobque_boost
require daslib/strings_boost
require daslib/dap
require daslib/algorithm
require daslib/debug_eval
let private
LOCAL_VARS = 1ul
ARGUMENT_VARS = 2ul
GLOBAL_VARS = 3ul
STATE_VARS = 4ul
// EVAL_POOL = 1000ul
BLOCKS_POOL = 4000ul
MAX_STACK = 1000ul
MAX_VARIABLES = 10_000_000ul
print_flags_debug = (
print_flags escapeString
| print_flags namesAndDimensions
| print_flags singleLine
// | print_flags humanReadable
// | print_flags typeQualifiers
// | print_flags refAddresses
)
def private ctx_at(var ctx: Context): DAContextAt
unsafe
return intptr(addr(ctx))
def describe(ti: rtti::TypeInfo)
unsafe
return describe(addr(ti))
def private get_int_arg(args: array<string>; name: string; def_val: int): int
let idx = find_index(args, name)
return idx >= 0 && idx + 1 < length(args) ? int(args[idx + 1]) : def_val
def private ends_with_separator(str: string): bool
for c in "\\/"
if ends_with(str, to_char(c))
return true
return false
def private starts_with_separator(str: string): bool
for c in "\\/"
if starts_with(str, to_char(c))
return true
return false
def private trim_path(path: string): string
if path == "."
return ""
if path |> starts_with("./") || path |> starts_with(".\\")
return path |> slice(2)
return path
def private join_path(a, path_b: string): string
let b = trim_path(path_b)
if length(a) == 0
return fix_path(b)
if length(b) == 0
return fix_path(a)
let res = build_string() <| $(builder)
builder |> write(a)
let ends = ends_with_separator(a)
let starts = starts_with_separator(b)
if ends && starts
builder |> write(slice(b, 1))
elif !ends && !starts
builder |> write("/")
builder |> write(b)
else
builder |> write(b)
return fix_path(res)
def private fix_path(path: string): string
return path |> trim_path() |> file_name_to_uri() |> normalize_uri() |> uri_to_file_name()
def private resolve_path(path: string; paths: array<string>): string
if path |> empty()
return path
for it in paths
let newPath = it |> join_path(path)
if stat(newPath).is_valid
return newPath
return fix_path(path)
def private resolve_path(path: string; paths: array<string>; aliases: table<string; string>): string
if path |> empty()
return path
for k, v in keys(aliases), values(aliases)
if path |> starts_with(k)
let fixedPath = v |> join_path <| slice(path, length(k))
return fixedPath |> resolve_path(paths)
return path |> resolve_path(paths)
def private resolve_path_cache(path: string; paths: array<string>; aliases: table<string; string>; var cache: table<string; string>): string
if cache |> key_exists(path)
return cache?[path] ?? ""
let absPath = resolve_path(path, paths, aliases)
cache[path] = absPath
return absPath
def private bytes_hr(value : uint64)
if value > uint64(1024 * 1024)
return "{float(value) / (1024f * 1024f)}mb ({int(value)})"
if value > uint64(1024)
return "{float(value) / 1024f}kb ({int(value)})"
return "{int(value)} b"
class DAWalker: DapiDataWalker
maxChildrenCount: uint = 500u
varsStack: array<DAVariable?>
[[do_not_delete]] frame: DAStackFrame?
visited: array<tuple<ps: void?; hash: uint64>>
inlinePreviewLimit: int = 50
def startWalk(var f: DAStackFrame; var v: DAVariable; cb: block<(): void>)
unsafe
frame = addr(f)
varsStack |> push(addr(v))
cb |> invoke()
frame = null
varsStack |> clear()
delete visited
def popStack()
let n = length(varsStack)
if n > 1
varsStack |> erase(n - 1)
def startCont()
let n = length(varsStack)
if varsStack[n-1].children == null
varsStack[n-1].children = new [[array<DAVariable>]]
def closeCont()
let n = length(varsStack)
if n > 0
var v & = unsafe(varsStack[n - 1])
if empty(v.value)
v.value = v._type
def override canVisitStructure(ps: void?; si: StructInfo): bool
for vis in visited
if vis.ps == ps && vis.hash == si.hash
return false
return true
def override beforeStructure(ps: void?; si: StructInfo): void
visited |> emplace([[auto ps, si.hash]])
def override beforeStructureField(ps:void?; si: StructInfo; pv: void?; vi:VarInfo; last: bool): void
self->startCont()
let n = length(varsStack)
if n > 0
var ti = type_info(vi)
unsafe
var v & = unsafe(varsStack[n - 1])
*v.children |> emplace([[DAVariable uid=frame.varId++, name=vi.name, _type=describe(ti), address=intptr(pv), size=ti.size ]])
varsStack |> push(addr((*v.children)[length(*v.children) - 1]))
def override afterStructureField(ps:void?; si: StructInfo; pv: void?; vi:VarInfo; last: bool): void
let n = length(varsStack)
if n > 0
var v & = unsafe(varsStack[n - 1])
if v.children == null
var ti = type_info(vi)
v.value = sprint_data_fast(pv, ti, print_flags_debug)
if empty(v.value)
v.value = v._type
self->popStack()
def override afterStructure(ps: void?; si: StructInfo): void
visited |> erase(length(visited) - 1)
self->closeCont()
let n = length(varsStack)
if n > 0
var v & = unsafe(varsStack[n - 1])
if v.children != null
v.value = join_with_names(*v.children, inlinePreviewLimit)
if empty(v.value)
v.value = v._type
def override afterStructureCancel(ps: void?; si: StructInfo): void
visited |> erase(length(visited) - 1)
self->closeCont()
let n = length(varsStack)
if n > 0
var v & = unsafe(varsStack[n - 1])
if v.children != null
v.value = join_with_names(*v.children, inlinePreviewLimit)
if empty(v.value)
v.value = v._type
def override canVisitArray(ps: void?; ti: TypeInfo): bool
let arr = unsafe(reinterpret<DapiArray?> ps)
return maxChildrenCount < 0u || arr.size <= maxChildrenCount
def override canVisitArrayData(ti: TypeInfo; count:uint): bool
return maxChildrenCount < 0u || count <= maxChildrenCount
def override beforeArrayData(ps: void?; stride: uint; count: uint; ti:TypeInfo): void
let n = length(varsStack)
if n > 0
var v & = unsafe(varsStack[n - 1])
v.indexedVariables = count
def override beforeArrayElement(ps: void?; ti: TypeInfo; pe: void?; index: uint; last: bool): void
self->startCont()
let n = length(varsStack)
if n > 0
unsafe
var v & = unsafe(varsStack[n - 1])
*v.children |> emplace([[DAVariable uid=frame.varId++, name="{int64(index)}", _type=describe(ti), address=intptr(pe), size=ti.size]])
varsStack |> push(addr((*v.children)[length(*v.children) - 1]))
def override afterArrayElement(ps: void?; ti: TypeInfo; pe: void?; index: uint; last: bool): void
let n = length(varsStack)
if n > 0
var v & = unsafe(varsStack[n - 1])
if v.children == null
unsafe
v.value = sprint_data_fast(pe, addr(ti), print_flags_debug)
if empty(v.value)
v.value = v._type
self->popStack()
def override afterArrayData(ps: void?; stride: uint; count: uint; ti: TypeInfo): void
let n = length(varsStack)
if n > 0
var v & = unsafe(varsStack[n - 1])
if count > 500u
print("IN VARIABLE {v.name} TOO MANY ELEMENTS {int64(count)}\n")
assert(false)
if v.children != null
let preview = join(*v.children, inlinePreviewLimit - 5)
if !empty(preview)
v.value = "[{int64(count)}] {preview}"
if empty(v.value)
v.value = "[{int64(count)}] {v._type}"
def override beforeTable(pa: DapiTable; ti: TypeInfo): void
let n = length(varsStack)
if n > 0
var v & = unsafe(varsStack[n - 1])
v.indexedVariables = pa.size
def override canVisitTable (ps: void?; ti: TypeInfo): bool
let pa = unsafe(reinterpret<DapiTable?> ps)
return maxChildrenCount < 0u || pa.size <= maxChildrenCount
def override beforeTableKey(pa: DapiTable; ti: TypeInfo; pk: void?; ki: TypeInfo; index: uint; last: bool): void
self->startCont()
let n = length(varsStack)
if n > 0
var v & = unsafe(varsStack[n - 1])
unsafe
*v.children |> emplace([[DAVariable uid=frame.varId++, name=sprint_data(pk, addr(ki), print_flags_debug), _type=describe(ki), address=intptr(pk), size=ki.size]])
varsStack |> push(addr((*v.children)[length(*v.children) - 1]))
def override beforeTableValue(pa: DapiTable; ti: TypeInfo; pv: void?; kv: TypeInfo; index: uint; last: bool): void
let n = length(varsStack)
if n > 0
var v & = unsafe(varsStack[n - 1])
v._type = describe(kv)
def override afterTableValue(pa: DapiTable; ti: TypeInfo; pv: void?; kv: TypeInfo; index: uint; last: bool): void
let n = length(varsStack)
if n > 0
var v & = unsafe(varsStack[n - 1])
if v.children == null
unsafe
v.value = sprint_data_fast(pv, addr(kv), print_flags_debug)
if empty(v.value)
v.value = v._type
self->popStack()
def override afterTable(pa: DapiTable; ti: TypeInfo): void
let n = length(varsStack)
if n > 0
var v & = unsafe(varsStack[n - 1])
if v.children != null
let preview = join_with_names(*v.children, inlinePreviewLimit - 5)
if !empty(preview)
v.value = "[{int64(pa.size)}] {preview}"
if empty(v.value)
v.value = "[{int64(pa.size)}] {v._type}"
tupleIndex: int = 0
def override beforeTuple(ps: void?; ti: TypeInfo): void
self->startCont()
tupleIndex = 0
def override beforeTupleEntry(ps: void?; ti: TypeInfo; pv: void?; vi: TypeInfo; last: bool): void
self->startCont()
let n = length(varsStack)
if n > 0
var v & = unsafe(varsStack[n - 1])
*v.children |> emplace([[DAVariable uid=frame.varId++, name="{tupleIndex++}", _type=describe(vi), address=intptr(pv), size=vi.size]])
unsafe
varsStack |> push(addr((*v.children)[length(*v.children) - 1]))
def override afterTupleEntry(ps: void?; ti: TypeInfo; pv: void?; vi: TypeInfo; last: bool): void
let n = length(varsStack)
if n > 0
var v & = unsafe(varsStack[n - 1])
if v.children == null
unsafe
v.value = sprint_data_fast(pv, addr(vi), print_flags_debug)
if empty(v.value)
v.value = v._type
self->popStack()
def override afterTuple(ps: void?; ti: TypeInfo): void
self->closeCont()
let n = length(varsStack)
if n > 0
var v & = unsafe(varsStack[n - 1])
if v.children != null
v.value = join(*v.children, inlinePreviewLimit)
if empty(v.value)
v.value = v._type
struct private DAVariable
uid: uint64
name: string
value: string
_type: string
typeInfo : TypeInfo const?
rawValue : float4
indexedVariables: uint
address: uint64
size: uint
children: array<DAVariable>?
def join_with_names(a: array<DAVariable>; limit: int): string
return build_string() <| $(str)
var first = true
var len = limit
let n = length(a)
for child, idx in a, range(n)
len -= length(child.name) + length(child.value) + 2
if !first
if len <= 3 && idx < n - 1
str |> write(",..")
break
str |> write(", ")
len -= 2
first = false
str |> write(child.name)
str |> write(": ")
str |> write(child.value)
def join(a: array<DAVariable>; limit: int): string
return build_string <| $(str)
var first = true
let n = length(a)
var len = limit
for v, idx in a, range(n)
len -= length(v.value)
if !first
if len <= 3 && idx < n - 1
str |> write(",..")
break
str |> write(", ")
len -= 2
first = false
str |> write(v.value)
def find_child_var(val: DAVariable; id: uint64; cb: block<(res:DAVariable): void>)
if val.uid == id
cb |> invoke(val)
return true
if val.children != null
for c in *val.children
if c |> find_child_var(id, cb)
return true
return false
def find_child_var(val: array<DAVariable>; cb: block<(res:DAVariable): bool>)
for v in val
if v |> find_child_var(cb)
return true
return false
def find_child_var(val: DAVariable; cb: block<(res:DAVariable): bool>)
if cb |> invoke(val)
return true
if val.children != null
for c in *val.children
if c |> find_child_var(cb)
return true
return false
struct private DAStackFrame
name: string
path: string
func: FuncInfo?
isBlock: bool = false
spAddr: uint64 = 0ul
line: uint = 1u
variables: array<DAVariable>
arguments: array<DAVariable>
globals: array<DAVariable>
state: array<tuple<uid: uint64; name:string; vars:array<DAVariable>>>
varId: uint64 = BLOCKS_POOL + 1ul
def find_child_var(stack: DAStackFrame; id: uint64; cb: block<(res:DAVariable): void>)
for c in stack.variables
if c |> find_child_var(id, cb)
return
for c in stack.arguments
if c |> find_child_var(id, cb)
return
for c in stack.globals
if c |> find_child_var(id, cb)
return
for c in stack.state
for v in c.vars
if v |> find_child_var(id, cb)
return
def find_child_var(stack: DAStackFrame; cb: block<(res:DAVariable; idx: uint64): bool>): bool
for c in stack.variables
if cb |> invoke(c, LOCAL_VARS)
return true
for c in stack.arguments
if cb |> invoke(c, ARGUMENT_VARS)
return true
for c in stack.globals
if cb |> invoke(c, GLOBAL_VARS)
return true
for idx, c in iter_range(stack.state), stack.state
for v in c.vars
if cb |> invoke(v, STATE_VARS + uint64(idx))
return true
return false
def iter_top_child_var(stack: DAStackFrame; cb: block<(res:DAVariable): void>)
for c in stack.variables
cb |> invoke(c)
for c in stack.arguments
cb |> invoke(c)
for c in stack.globals
cb |> invoke(c)
// for c in stack.state
// cb |> invoke(c)
class private DAStackWalker: DapiStackWalker
dataWalkerAdapter: smart_ptr<DataWalker>
dataWalker: DAWalker?
collectAllGlobals : bool = false
wasGlobalsCollected : bool = false
[[do_not_delete]] ctx: DAContext?
[[do_not_delete]] workingPaths: array<string>?
[[do_not_delete]] pathAliases: table<string; string>?
[[do_not_delete]] pathsCache: table<string; string>?
def DAStackWalker()
dataWalker = new DAWalker()
unsafe
dataWalkerAdapter <- make_data_walker(dataWalker)
def operator delete
unsafe
delete dataWalkerAdapter
delete dataWalker
def collectGlobalAt(vinfo:VarInfo; var frame : DAStackFrame&)
var value: void?
unsafe
value = get_context_global_variable(ctx.ctx, vinfo.name)
let ti = type_info(vinfo)
var global = [[DAVariable uid=frame.varId++, name=vinfo.name, _type=describe(ti), address=intptr(value), size=ti.size, typeInfo=ti]]
if ti != null
dataWalker->startWalk(frame, global) <| $
dataWalkerAdapter |> walk_data(value, *ti)
if global.children == null
global.value = sprint_data_fast(value, ti, print_flags_debug)
if empty(global.value)
global.value = global._type
frame.globals |> emplace(global)
def collectGlobals()
if wasGlobalsCollected
return
wasGlobalsCollected = true
let stackLen = length(ctx.stack)
if stackLen == 0
return
var frame & = unsafe(ctx.stack[stackLen-1])
if collectAllGlobals
for i in range(get_total_variables(*ctx.ctx))
let vinfo & = unsafe(get_variable_info(ctx.ctx, i))
if vinfo.name |> starts_with("g```") || vinfo.name == "__rtti_require"
continue
collectGlobalAt(vinfo, frame)
return
if frame.func != null
for i in range(frame.func.globalCount)
let vinfo = unsafe(frame.func.globals[i])
if vinfo.name |> starts_with("g```") || vinfo.name == "__rtti_require"
continue
// if vinfo.scope == frame.func
collectGlobalAt(*vinfo, frame)
def override onCallAOT(pp:Prologue; fileName:string#): void
// print("AOT {fileName}\n")
let n = length(ctx.stack)
if n > 0
ctx.stack[n - 1].name = pp.info != null ? "def {pp.info.name} [AOT]" : "def [AOT]"
ctx.stack[n - 1].path = "{fileName}"
self->collectGlobals()
def override onCallAt(pp:Prologue; info:FuncInfo; at:LineInfo): void
// print("def {info.name} at {at}\n")
let n = length(ctx.stack)
if n > 0
ctx.stack[n - 1].name = "def {info.name}"
ctx.stack[n - 1].path = "{at.fileInfo.name}" |> resolve_path_cache(*workingPaths, *pathAliases, *pathsCache)
ctx.stack[n - 1].line = at.line
self->collectGlobals()
def override onCall(pp:Prologue; info:FuncInfo): void
// print("def {info.name}\n")
let n = length(ctx.stack)
if n > 0
ctx.stack[n - 1].name = "def {info.name}"
self->collectGlobals()
def override onBeforeCall(pp: Prologue; sp: void?): void
let iblock = intptr(pp._block)
let isBlock = (iblock & 1ul) != 0ul
let funcInfo = isBlock ? null : pp.info
ctx.stack |> emplace([[DAStackFrame() func=funcInfo, isBlock=isBlock, spAddr=intptr(sp) ]])
def override onVariable(inf: FuncInfo; vinfo: LocalVariableInfo; arg: void?; inScope: bool): void
if !inScope
return
let stackLen = length(ctx.stack)
if stackLen > 0
var frame & = unsafe(ctx.stack[stackLen - 1])
var ti = type_info(vinfo)
var variable = [[DAVariable uid=frame.varId++, name=vinfo.name, _type=describe(ti), address=intptr(arg), size=ti.size, typeInfo=ti]]
if ti != null
dataWalker->startWalk(frame, variable) <| $
dataWalkerAdapter |> walk_data(arg, *ti)
if variable.children == null
let value = !inScope ? "<uninitialized>" : arg != null ? sprint_data_fast(arg, ti, print_flags_debug): "<optimized>"
variable.value = value
elif empty(variable.value)
variable.value = variable._type
frame.variables |> emplace(variable)
def override onArgument (inf: FuncInfo; index: int; vinfo: VarInfo; arg: float4)
let stackLen = length(ctx.stack)
if stackLen > 0
var frame & = unsafe(ctx.stack[stackLen-1])
let ti = type_info(vinfo)
var variable = [[DAVariable uid=frame.varId++, name=vinfo.name, _type=describe(ti), address=ti.isRef ? intptr(unsafe(reinterpret<void?>arg)) : 0ul, size=ti.size, typeInfo=ti, rawValue=arg]]
if ti != null
dataWalker->startWalk(frame, variable) <| $
dataWalkerAdapter |> walk_data(arg, *ti)
if variable.children == null
variable.value = sprint_data_fast(arg, ti, print_flags_debug)
if empty(variable.value)
variable.value = variable._type
frame.arguments |> emplace(variable)
enum DABreakpointState
Uninitialized = 0
Initialized
Instrumented
typedef
DAContextAt = uint64
struct private DABreakpoint
id: uint64
line: uint
prevState: DABreakpointState
state: DABreakpointState
ctxAt : DAContextAt
typedef
DABreakpoints = table<string; array<DABreakpoint>>
struct private DAContext
id: uint64
at: DAContextAt
ctx: Context?
stack: array<DAStackFrame>
continueRequested: bool = false
pauseRequested: bool = false
stepInRequested: bool = false
stepRequestedStack: int = 0 // stack depth
def private reset_debug_flags(var ctx: DAContext)
ctx.continueRequested = false
ctx.pauseRequested = false
ctx.stepInRequested = false
ctx.stepRequestedStack = 0
def private to_string(ctx: DAContext)
return build_string() <| $(str)
if ctx.ctx.category.debugger_attached
str |> write("*")
let ctxNameLen = length(ctx.ctx.name)
if ctxNameLen > 0
str |> write(ctx.ctx.name)
else
str |> write(ctx.at)
if int(ctx.ctx.category) > 0
str |> write(" ")
str |> write(ctx.ctx.category)
if ctxNameLen > 0
str |> write(" ")
str |> write(ctx.at)
str |> write(" (")
str |> write(int64(ctx.id))
str |> write(")")
def private compare_path(p1, p2: string)
if p1 == p2
return true
return to_lower(p1) == to_lower(p2)
class private DAgent: DapiDebugAgent
walkerAdapter: smart_ptr<StackWalker>
walker: DAStackWalker?
server: DAServer?
breakpoints: DABreakpoints
breakpointId: uint64 = 1ul
contexts: array<DAContext>
contextId: uint64 = 1ul
waitConnection: bool = true
withInstruments: bool = false
workingPaths: array<string>
pathAliases: table<string; string>
pathsCache: table<string; string>
collectingAgentData: bool = false
logStrMemory = false
initialStrLimit = uint64(8 * 1024 * 1024)
strLimit: uint64 = uint64(8 * 1024 * 1024)
pauseCtx: rtti::Context? = null
pauseAt: LineInfo const? = null
evalFrame: DAStackFrame <- DAStackFrame()
hwBreakpoints: array<int>
def initAgent(var ctx: Context)
waitConnection = wait_debugger()
withInstruments = use_instruments()
walker = new DAStackWalker()
unsafe
walkerAdapter <- make_stack_walker(walker)
walker.workingPaths = addr(workingPaths)
walker.pathAliases = addr(pathAliases)
walker.pathsCache = addr(pathsCache)
self->startServer()
self->addContext(ctx)
self->addContext(this_context())
def startServer()
let args <- get_command_line_arguments()
let port = args |> get_int_arg("--das-debug-port", 10000)
server = new DAServer()
server.port = port
unsafe
server.agent = addr(self)
server->make_server_adapter()
if !server->init(port)
to_log(LOG_ERROR, "debug server failed to initialize\n")
unsafe
delete server
def addPath(path: string)
if workingPaths |> find_index(path) < 0
workingPaths |> push(path)
def setPaths(paths: array<string>; aliases: table<string; string>)
workingPaths |> clear()
pathsCache |> clear()
for path in paths
self->addPath(path)
let dasRoot = get_das_root()
if !empty(dasRoot) && dasRoot != "."
self->addPath(dasRoot |> join_path("daslib"))
self->addPath(dasRoot |> join_path("src/builtin"))
pathAliases |> clear()
for k, v in keys(aliases), values(aliases)
pathAliases[k] = v
def override onUninstall(agent:DebugAgent?)
if agent == thisAgent
delete self
def operator delete
unsafe
delete walkerAdapter
delete walker
delete server
def log(msg: string)
if server != null
server->log(msg)
else
print("{msg}\n")
def reqPause(var ctx: DAContext)
ctx |> reset_debug_flags()
ctx.pauseRequested = true
if withInstruments
for c in contexts
if !c.ctx.category.debug_context && !ctx.ctx.category.debugger_tick
*c.ctx |> set_single_step(true)
def reqResume(var exclude: DAContext)
if withInstruments
for ctx in contexts
if ctx.id != exclude.id && !ctx.ctx.category.debug_context
*ctx.ctx |> set_single_step(false)
def onBreakpointsChanged(ini: SetBreakpointsArguments): SetBreakpointsResponse
let path = ini.source.path
var res: SetBreakpointsResponse
if key_exists(breakpoints, path)
delete breakpoints[path]
on_breakpoints_reset(path, length(ini.breakpoints))
breakpoints |> erase(path)
if length(ini.breakpoints) >= 0
var breaks <- [[array<DABreakpoint>]]
for b in ini.breakpoints
breaks |> emplace([[DABreakpoint line=uint(b.line), id=breakpointId ]])
res.breakpoints |> emplace([[Breakpoint
verified=!withInstruments,
id=double(breakpointId),
source=ini.source,
line=b.line
]])
breakpointId += 1ul
breakpoints[path] <- breaks
if !withInstruments
return <- res
for resBr in res.breakpoints
resBr.message = "{path}: instrumentation wasn't initined"
for ctx in contexts
if ctx.ctx.category.debug_context || ctx.ctx.category.debugger_tick
continue
*ctx.ctx |> clear_instruments()
*ctx.ctx |> instrument_node(true) <| $(ati)
if ati.fileInfo == null
return false
let file = string(ati.fileInfo.name) |> resolve_path_cache(workingPaths, pathAliases, pathsCache)
let currentPath = compare_path(file, path)
if currentPath
for resBr in res.breakpoints
if !resBr.verified
resBr.message = "{path}: instumentation is inited, but this line is without instructions"
for fileBr, brs in keys(breakpoints), values(breakpoints)
if compare_path(fileBr, file)
for br in brs
if br.state == DABreakpointState Uninitialized
br.state = DABreakpointState Initialized
for br in brs
if br.line == ati.line
if currentPath
for resBr in res.breakpoints
resBr.verified ||= uint(resBr.line) == br.line
if resBr.verified
br.state = DABreakpointState Instrumented
resBr.message = ""
return true
break
return false
return <- res
def override onInstrument(var ctx: Context; at: LineInfo): void
self->onPause(ctx, at, "breakpoint", "")
def override onBreakpoint(var ctx: Context; at:LineInfo; reason, text:string): void
if reason == "exception"
self->getContextDataFor(ctx) <| $(var ctxData: DAContext)
ctxData |> reset_debug_flags()
self->onPause(ctx, at, reason, text)
def beforePause(ctxData: DAContext; at: LineInfo)
pauseCtx = ctxData.ctx
pauseAt = unsafe(addr(at))
evalFrame.varId = 1000ul //EVAL_POOL
delete evalFrame.variables
def afterPause()
pauseCtx = null
pauseAt = null
def onPause(var ctx: Context; at: LineInfo; reason, text: string): void
if at.fileInfo == null
return
if server == null
return
if !waitConnection && (!server.configurationDone || !server.threadsDone)
return
let path = "{at.fileInfo.name}" |> resolve_path_cache(workingPaths, pathAliases, pathsCache)
self->log("{ctx} `{reason}` breakpoint at {path}:{int(at.line)}\n")
self->addContext(ctx)
self->getContextDataFor(ctx) <| $(var ctxData: DAContext)
ctx |> set_single_step(true)
self->sendStopped(ctx, ctxData, path, at, reason, text, -1ul)
while !ctxData.continueRequested && !ctxData.stepInRequested && ctxData.stepRequestedStack == 0
tick_debugger()
ctxData.continueRequested = false
self->afterPause()
ctx |> set_single_step(!withInstruments || ctxData.stepInRequested || ctxData.stepRequestedStack > 0)
def override onSingleStep(var ctx: Context; at: LineInfo): void
if at.fileInfo == null
return
if server == null
return
// unsafe
// self->log("{double(ctx_at(ctx))} step at {string(at.fileInfo.name) |> resolve_path_cache(workingPaths, pathAliases, pathsCache)}:{int(at.line)}\n")
// print("{double(ctx_at(ctx))} step at {string(at.fileInfo.name) |> resolve_path_cache(workingPaths, pathAliases, pathsCache)}:{int(at.line)}\n")
if ctx.category.debugger_attached
self->addContext(ctx)
if waitConnection && (!server.configurationDone || !server.threadsDone)
if ctx.category.debugger_attached
while !server.configurationDone || !server.threadsDone
tick_debugger()
else
self->log("[E] non debug context in single step {ctx.name}")
ctx |> set_single_step(!withInstruments)
self->getContextDataFor(ctx) <| $(var ctxData: DAContext)
let file = string(at.fileInfo.name) |> resolve_path_cache(workingPaths, pathAliases, pathsCache)
if ctxData.pauseRequested
self->sendStopped(ctx, ctxData, file, at, "pause", "", -1ul)
while ctxData.pauseRequested
tick_debugger()
self->afterPause()
self->reqResume(ctxData)
if ctxData.stepInRequested
ctxData.stepInRequested = false
self->sendStopped(ctx, ctxData, file, at, "step", "", -1ul)
while !ctxData.continueRequested && !ctxData.stepInRequested && ctxData.stepRequestedStack == 0
tick_debugger()
ctxData.continueRequested = false
self->afterPause()
if ctxData.stepRequestedStack > 0
if stack_depth(*ctxData.ctx) > ctxData.stepRequestedStack
return
ctxData.stepRequestedStack = 0
self->sendStopped(ctx, ctxData, file, at, "step", "", -1ul)
while !ctxData.continueRequested && !ctxData.stepInRequested && ctxData.stepRequestedStack == 0
tick_debugger()
self->afterPause()
ctxData.continueRequested = false
if withInstruments
ctx |> set_single_step(ctxData.stepInRequested || ctxData.stepRequestedStack > 0)
return
// self->log("search break `{file}`:{at.line} - {inBreak}\n")
var i = 0
while i < length(breakpoints)
var fileBr: string
for j, it in range(i + 1), keys(breakpoints)
if j == i
fileBr = it
break
i += 1
if !compare_path(fileBr, file)
continue
breakpoints |> get(file) <| $(brs)
var brIdx = 0
while brIdx < length(brs)
let br & = unsafe((brs)[brIdx++])
// self->log("search break `{file}` {brs} in {file}\n")
if br.line != uint(at.line)
continue
self->sendStopped(ctx, ctxData, file, at, "breakpoint", "", br.id)
ctxData.continueRequested = false
while !ctxData.continueRequested && !ctxData.stepInRequested && ctxData.stepRequestedStack == 0
tick_debugger()
ctxData.continueRequested = false
self->afterPause()
break
def nextStep(var ctx: Context; var ctxData:DAContext; path: string; at:LineInfo)
delete ctxData.stack
walker.wasGlobalsCollected = false
unsafe
walker.ctx = addr(ctxData)
walkerAdapter |> walk_stack(ctx, at)
if length(ctxData.stack) > 0
for i in range(1, length(ctxData.stack))
let j = length(ctxData.stack) - i
let q = j - 1
ctxData.stack[j].path = ctxData.stack[q].path
ctxData.stack[j].line = ctxData.stack[q].line
ctxData.stack[0].path = path
ctxData.stack[0].line = at.line
if !collectingAgentData
collectingAgentData = true
collect_debug_agent_state(ctx, at)
collectingAgentData = false
def override onVariable(var ctx: Context; category, name: string; info: TypeInfo; data: void?): void
self->getContextDataFor(ctx) <| $(var ctxData: DAContext)
let stackLen = length(ctxData.stack)
if stackLen > 0
var frame & = unsafe(ctxData.stack[0])
var variable = [[DAVariable uid=frame.varId++, name=name, _type=describe(info), address=intptr(data), size=info.size]]
walker.dataWalker->startWalk(frame, variable) <| $
walker.dataWalkerAdapter |> walk_data(data, info)
if variable.children == null