@@ -20,6 +20,7 @@ export enum BpType {
2020 Instruction = 'instruction' ,
2121 FunctionEntry = 'function entry' ,
2222 FunctionExit = 'function exit' ,
23+ FunctionTrace = 'function trace' ,
2324 MemoryRead = 'memory read' ,
2425 MemoryWrite = 'memory write' ,
2526}
@@ -32,10 +33,12 @@ export class Bp {
3233 private _addr : Var | null ;
3334 private _literal : string | null ;
3435 private _length : number ;
36+ private _depth : number ;
3537
3638 private _lines : string [ ] = [ ] ;
3739 private _listener : InvocationListener | null ;
3840 private _overlay : string | null = null ;
41+ private _trace : ArrayBuffer = new ArrayBuffer ( 0 ) ;
3942
4043 public constructor (
4144 type : BpType ,
@@ -44,13 +47,15 @@ export class Bp {
4447 addr : Var | null ,
4548 literal : string | null ,
4649 length : number = 0 ,
50+ depth : number = 0 ,
4751 ) {
4852 this . _type = type ;
4953 this . _idx = idx ;
5054 this . _hits = hits ;
5155 this . _addr = addr ;
5256 this . _literal = literal ;
5357 this . _length = length ;
58+ this . _depth = depth ;
5459 this . _listener = null ;
5560 }
5661
@@ -74,8 +79,8 @@ export class Bp {
7479 case BpType . Instruction :
7580 case BpType . FunctionEntry :
7681 case BpType . FunctionExit :
82+ case BpType . FunctionTrace :
7783 return BpKind . Code ;
78- break ;
7984 case BpType . MemoryRead :
8085 case BpType . MemoryWrite :
8186 return BpKind . Memory ;
@@ -116,11 +121,155 @@ export class Bp {
116121 } ,
117122 } ) ;
118123 break ;
124+ case BpType . FunctionTrace :
125+ this . _listener = Interceptor . attach ( addr . toPointer ( ) , {
126+ onEnter ( ) {
127+ bp . startCoverage ( this . threadId , this . context ) ;
128+ } ,
129+ onLeave ( _retVal ) {
130+ bp . stopCoverage ( this . threadId , this . context ) ;
131+ } ,
132+ } ) ;
119133 }
120134
121135 Interceptor . flush ( ) ;
122136 }
123137
138+ private displayEvents ( ) {
139+ const events = Stalker . parse ( this . _trace , {
140+ annotate : true ,
141+ stringify : false ,
142+ } ) as StalkerEventFull [ ] ;
143+
144+ let currentDepth = 0 ;
145+ let first = true ;
146+ for ( const e of events ) {
147+ switch ( e . length ) {
148+ case 3 : {
149+ const [ kind , start , _end ] = e ;
150+ if ( currentDepth >= this . _depth ) break ;
151+ if ( kind !== 'block' ) break ;
152+ const name = this . getAddressString ( start as NativePointer ) ;
153+ if ( name === null ) break ;
154+ if ( first ) {
155+ currentDepth = 0 ;
156+ first = false ;
157+ }
158+ if ( currentDepth > 0 ) {
159+ Output . write ( '\t' . repeat ( currentDepth ) ) ;
160+ }
161+ Output . writeln ( name ) ;
162+ break ;
163+ }
164+ case 4 : {
165+ const [ kind , _from , _to , _depth ] = e ;
166+ if ( kind === 'call' ) {
167+ currentDepth += 1 ;
168+ } else if ( kind === 'ret' ) {
169+ if ( currentDepth > 0 ) {
170+ currentDepth -= 1 ;
171+ }
172+ }
173+ break ;
174+ }
175+ default :
176+ break ;
177+ }
178+ }
179+ }
180+
181+ private getAddressString ( address : NativePointer ) : string | null {
182+ const debug = DebugSymbol . fromAddress ( address ) ;
183+ if ( debug === null || debug . name === null ) {
184+ const module = Process . findModuleByAddress ( address ) ;
185+ if ( module === null ) {
186+ return null ;
187+ }
188+
189+ const offset = address . sub ( module . base ) ;
190+ const prefix = `${ module . name } +0x${ offset . toString ( 16 ) } ` ;
191+ return `${ Output . green ( prefix . padEnd ( 40 , '.' ) ) } ${ Output . yellow ( Format . toHexString ( address ) ) } ` ;
192+ }
193+
194+ const lookup = DebugSymbol . fromName ( debug . name ) ;
195+ let offset = ptr ( 0 ) ;
196+ if ( lookup !== null && lookup . address . compare ( debug . address ) < 0 ) {
197+ offset = debug . address . sub ( lookup . address ) ;
198+ }
199+ const OFFSET_MAX = 1024 ;
200+ const prefix = debug . moduleName === null ? '' : `${ debug . moduleName } !` ;
201+
202+ let name = `${ prefix } ${ debug . name } ` ;
203+ if ( offset !== ptr ( 0 ) || offset . compare ( OFFSET_MAX ) < 0 ) {
204+ name = `${ prefix } ${ debug . name } +0x${ offset . toString ( 16 ) } ` ;
205+ }
206+
207+ const symbol = `${ Output . green ( name . padEnd ( 40 , '.' ) ) } ${ Output . yellow ( Format . toHexString ( debug . address ) ) } ` ;
208+ if ( debug . fileName !== null && debug . lineNumber !== null ) {
209+ if ( debug . fileName . length !== 0 && debug . lineNumber !== 0 ) {
210+ return `${ symbol } ${ Output . blue ( debug . fileName ) } :${ Output . blue ( debug . lineNumber . toString ( ) ) } ` ;
211+ }
212+ }
213+ return symbol ;
214+ }
215+
216+ private startCoverage ( threadId : ThreadId , ctx : CpuContext ) {
217+ if ( this . _hits === 0 ) return ;
218+ Output . clearLine ( ) ;
219+ Output . writeln ( Output . yellow ( '-' . repeat ( 80 ) ) ) ;
220+ Output . write ( `${ Output . yellow ( '|' ) } Start Trace ` ) ;
221+ Output . write ( `${ Output . green ( `#${ this . _idx } ` ) } ` ) ;
222+ Output . write ( `[${ this . _type } ] ` ) ;
223+ Output . write ( `${ Output . yellow ( this . _literal ?? '' ) } ` ) ;
224+ Output . write ( `@ $pc=${ Output . blue ( Format . toHexString ( ctx . pc ) ) } ` ) ;
225+ Output . write ( `$tid=${ threadId } , depth=${ this . _depth } ` ) ;
226+ Output . writeln ( ) ;
227+ Output . writeln ( Output . yellow ( '-' . repeat ( 80 ) ) ) ;
228+
229+ this . _trace = new ArrayBuffer ( 0 ) ;
230+
231+ Stalker . follow ( threadId , {
232+ events : {
233+ call : true ,
234+ ret : true ,
235+ block : true ,
236+ } ,
237+ onReceive : ( events : ArrayBuffer ) => {
238+ const newBuffer = new Uint8Array (
239+ this . _trace . byteLength + events . byteLength ,
240+ ) ;
241+ newBuffer . set ( new Uint8Array ( this . _trace ) , 0 ) ;
242+ newBuffer . set ( new Uint8Array ( events ) , this . _trace . byteLength ) ;
243+ this . _trace = newBuffer . buffer as ArrayBuffer ;
244+ } ,
245+ } ) ;
246+ }
247+
248+ private stopCoverage ( threadId : ThreadId , ctx : CpuContext ) {
249+ if ( this . _hits === 0 ) return ;
250+ else if ( this . _hits > 0 ) this . _hits -- ;
251+ try {
252+ Stalker . unfollow ( threadId ) ;
253+ Stalker . flush ( ) ;
254+ Output . writeln ( Output . blue ( '-' . repeat ( 80 ) ) ) ;
255+ this . displayEvents ( ) ;
256+ Output . writeln ( Output . blue ( '-' . repeat ( 80 ) ) ) ;
257+ Output . clearLine ( ) ;
258+ Output . writeln ( Output . yellow ( '-' . repeat ( 80 ) ) ) ;
259+ Output . write ( `${ Output . yellow ( '|' ) } Stop Trace ` ) ;
260+ Output . write ( `${ Output . green ( `#${ this . _idx } ` ) } ` ) ;
261+ Output . write ( `[${ this . _type } ] ` ) ;
262+ Output . write ( `${ Output . yellow ( this . _literal ?? '' ) } ` ) ;
263+ Output . write ( `@ $pc=${ Output . blue ( Format . toHexString ( ctx . pc ) ) } ` ) ;
264+ Output . write ( `$tid=${ threadId } ` ) ;
265+ Output . writeln ( ) ;
266+ Output . writeln ( Output . yellow ( '-' . repeat ( 80 ) ) ) ;
267+ } finally {
268+ Input . prompt ( ) ;
269+ Regs . clear ( ) ;
270+ }
271+ }
272+
124273 private breakCode (
125274 threadId : ThreadId ,
126275 ctx : CpuContext ,
@@ -310,6 +459,10 @@ export class Bp {
310459 return this . _length ;
311460 }
312461
462+ public get depth ( ) : number | null {
463+ return this . _depth ;
464+ }
465+
313466 public get hits ( ) : number {
314467 return this . _hits ;
315468 }
@@ -329,6 +482,11 @@ export class Bp {
329482 this . _length = length ;
330483 }
331484
485+ public set depth ( depth : number | null ) {
486+ if ( depth === null ) return ;
487+ this . _depth = depth ;
488+ }
489+
332490 public set hits ( hits : number ) {
333491 this . _hits = hits ;
334492 }
0 commit comments