@@ -6,8 +6,11 @@ import { Overlay } from '../memory/overlay.js';
66import { Parser } from '../io/parser.js' ;
77import { Regs } from './regs.js' ;
88import { Format } from '../misc/format.js' ;
9+ import { BlockTrace } from '../traces/block.js' ;
10+ import { Trace , Traces } from '../traces/trace.js' ;
911import { Var } from '../vars/var.js' ;
1012import { Vars } from '../vars/vars.js' ;
13+ import { CallTrace } from '../traces/call.js' ;
1114
1215export const BP_LENGTH : number = 16 ;
1316
@@ -20,7 +23,9 @@ export enum BpType {
2023 Instruction = 'instruction' ,
2124 FunctionEntry = 'function entry' ,
2225 FunctionExit = 'function exit' ,
23- FunctionTrace = 'function trace' ,
26+ BlockTrace = 'block trace' ,
27+ CallTrace = 'call trace' ,
28+ UniqueBlockTrace = 'unique block trace' ,
2429 MemoryRead = 'memory read' ,
2530 MemoryWrite = 'memory write' ,
2631}
@@ -38,7 +43,7 @@ export class Bp {
3843 private _lines : string [ ] = [ ] ;
3944 private _listener : InvocationListener | null ;
4045 private _overlay : string | null = null ;
41- private _trace : ArrayBuffer = new ArrayBuffer ( 0 ) ;
46+ private _trace : Trace | null = null ;
4247
4348 public constructor (
4449 type : BpType ,
@@ -79,7 +84,9 @@ export class Bp {
7984 case BpType . Instruction :
8085 case BpType . FunctionEntry :
8186 case BpType . FunctionExit :
82- case BpType . FunctionTrace :
87+ case BpType . BlockTrace :
88+ case BpType . CallTrace :
89+ case BpType . UniqueBlockTrace :
8390 return BpKind . Code ;
8491 case BpType . MemoryRead :
8592 case BpType . MemoryWrite :
@@ -121,100 +128,53 @@ export class Bp {
121128 } ,
122129 } ) ;
123130 break ;
124- case BpType . FunctionTrace :
131+ case BpType . BlockTrace :
125132 this . _listener = Interceptor . attach ( addr . toPointer ( ) , {
126133 onEnter ( ) {
134+ if ( bp . _hits === 0 ) return ;
135+ bp . _trace = BlockTrace . create ( this . threadId , bp . _depth ) ;
127136 bp . startCoverage ( this . threadId , this . context ) ;
128137 } ,
129138 onLeave ( _retVal ) {
139+ if ( bp . _hits === 0 ) return ;
130140 bp . stopCoverage ( this . threadId , this . context ) ;
131141 } ,
132142 } ) ;
143+ break ;
144+ case BpType . CallTrace :
145+ this . _listener = Interceptor . attach ( addr . toPointer ( ) , {
146+ onEnter ( ) {
147+ if ( bp . _hits === 0 ) return ;
148+ bp . _trace = CallTrace . create ( this . threadId , bp . _depth ) ;
149+ bp . startCoverage ( this . threadId , this . context ) ;
150+ } ,
151+ onLeave ( _retVal ) {
152+ if ( bp . _hits === 0 ) return ;
153+ bp . stopCoverage ( this . threadId , this . context ) ;
154+ } ,
155+ } ) ;
156+ break ;
157+ case BpType . UniqueBlockTrace :
158+ this . _listener = Interceptor . attach ( addr . toPointer ( ) , {
159+ onEnter ( ) {
160+ if ( bp . _hits === 0 ) return ;
161+ bp . _trace = BlockTrace . create ( this . threadId , bp . _depth , true ) ;
162+ bp . startCoverage ( this . threadId , this . context ) ;
163+ } ,
164+ onLeave ( _retVal ) {
165+ if ( bp . _hits === 0 ) return ;
166+ bp . stopCoverage ( this . threadId , this . context ) ;
167+ } ,
168+ } ) ;
169+ break ;
170+ default :
171+ throw new Error ( `unknown code breakpoint type: ${ this . _type } ` ) ;
133172 }
134173
135174 Interceptor . flush ( ) ;
136175 }
137176
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-
216177 private startCoverage ( threadId : ThreadId , ctx : CpuContext ) {
217- if ( this . _hits === 0 ) return ;
218178 Output . clearLine ( ) ;
219179 Output . writeln ( Output . yellow ( '-' . repeat ( 80 ) ) ) ;
220180 Output . write ( `${ Output . yellow ( '|' ) } Start Trace ` ) ;
@@ -225,36 +185,19 @@ export class Bp {
225185 Output . write ( `$tid=${ threadId } , depth=${ this . _depth } ` ) ;
226186 Output . writeln ( ) ;
227187 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- } ) ;
246188 }
247189
248190 private stopCoverage ( threadId : ThreadId , ctx : CpuContext ) {
249- if ( this . _hits === 0 ) return ;
250- else if ( this . _hits > 0 ) this . _hits -- ;
191+ this . _hits -- ;
251192 try {
252- Stalker . unfollow ( threadId ) ;
253- Stalker . flush ( ) ;
193+ if ( this . _trace === null ) return ;
194+ this . _trace . stop ( ) ;
195+
254196 Output . writeln ( Output . blue ( '-' . repeat ( 80 ) ) ) ;
255- this . displayEvents ( ) ;
197+ this . _trace . display ( ) ;
256198 Output . writeln ( Output . blue ( '-' . repeat ( 80 ) ) ) ;
257199 Output . clearLine ( ) ;
200+
258201 Output . writeln ( Output . yellow ( '-' . repeat ( 80 ) ) ) ;
259202 Output . write ( `${ Output . yellow ( '|' ) } Stop Trace ` ) ;
260203 Output . write ( `${ Output . green ( `#${ this . _idx } ` ) } ` ) ;
@@ -264,6 +207,8 @@ export class Bp {
264207 Output . write ( `$tid=${ threadId } ` ) ;
265208 Output . writeln ( ) ;
266209 Output . writeln ( Output . yellow ( '-' . repeat ( 80 ) ) ) ;
210+
211+ Traces . delete ( threadId ) ;
267212 } finally {
268213 Input . prompt ( ) ;
269214 Regs . clear ( ) ;
0 commit comments