@@ -16,6 +16,7 @@ package instrument
16
16
17
17
import (
18
18
"fmt"
19
+ "go/parser"
19
20
"path/filepath"
20
21
"regexp"
21
22
"sort"
@@ -28,15 +29,7 @@ import (
28
29
)
29
30
30
31
const (
31
- TrampolineJumpIfDesc = "/* TRAMPOLINE_JUMP_IF */"
32
- TrampolineJumpIfDescRegexp = "/\\ * TRAMPOLINE_JUMP_IF \\ */"
33
- TrampolineNoNewlinePlaceholder = "/* NO_NEWWLINE_PLACEHOLDER */"
34
- TrampolineNoNewlinePlaceholderRegexp = "/\\ * NO_NEWWLINE_PLACEHOLDER \\ */\n "
35
- TrampolineSemicolonPlaceholder = "/* SEMICOLON_PLACEHOLDER */"
36
- TrampolineSemicolonPlaceholderRegexp = "/\\ * SEMICOLON_PLACEHOLDER \\ */\n "
37
- )
38
-
39
- const (
32
+ TJumpLabel = "/* TRAMPOLINE_JUMP_IF */"
40
33
OtelAPIFile = "otel_api.go"
41
34
OtelTrampolineFile = "otel_trampoline.go"
42
35
)
@@ -54,10 +47,15 @@ func (rp *RuleProcessor) copyOtelApi(pkgName string) error {
54
47
55
48
func (rp * RuleProcessor ) loadAst (filePath string ) (* dst.File , error ) {
56
49
file := rp .tryRelocated (filePath )
57
- return util .ParseAstFromFile (file )
50
+ rp .parser = util .NewAstParser ()
51
+ var err error
52
+ rp .target , err = rp .parser .ParseFile (file , parser .ParseComments )
53
+ return rp .target , err
58
54
}
59
55
60
56
func (rp * RuleProcessor ) restoreAst (filePath string , root * dst.File ) (string , error ) {
57
+ rp .parser = nil
58
+ rp .target = nil
61
59
filePath = rp .tryRelocated (filePath )
62
60
name := filepath .Base (filePath )
63
61
newFile , err := util .WriteAstToFile (root , filepath .Join (rp .workDir , name ))
@@ -87,7 +85,7 @@ func (rp *RuleProcessor) makeName(r *resource.InstFuncRule, onEnter bool) string
87
85
func findJumpPoint (jumpIf * dst.IfStmt ) * dst.BlockStmt {
88
86
// Multiple func rules may apply to the same function, we need to find the
89
87
// appropriate jump point to insert trampoline jump.
90
- if len (jumpIf .Decs .If ) == 1 && jumpIf .Decs .If [0 ] == TrampolineJumpIfDesc {
88
+ if len (jumpIf .Decs .If ) == 1 && jumpIf .Decs .If [0 ] == TJumpLabel {
91
89
// Insert trampoline jump within the else block
92
90
elseBlock := jumpIf .Else .(* dst.BlockStmt )
93
91
if len (elseBlock .List ) > 1 {
@@ -181,32 +179,9 @@ func (rp *RuleProcessor) insertTJump(t *resource.InstFuncRule,
181
179
ifStmt : tjump ,
182
180
rule : t ,
183
181
})
184
-
185
- // @@ Unfortunately, dst framework does not support fine-grained space control
186
- // i.e. there is no way to generate all above AST into one line code, we have
187
- // to manually format it. There we insert OtelNewlineTrampolineHolder anchor
188
- // so that we aware of where we should remove trailing newline.
189
- // if .... { /* NO_NEWWLINE_PLACEHOLDER */
190
- // ... /* NO_NEWWLINE_PLACEHOLDER */
191
- // } else { /* NO_NEWWLINE_PLACEHOLDER */
192
- // ... /* SEMICOLON_PLACEHOLDER */
193
- // } /* NO_NEWWLINE_PLACEHOLDER */
194
- // NEW_LINE
195
- { // then block
196
- callExpr := tjump .Body .List [0 ]
197
- callExpr .Decorations ().Start .Append (TrampolineNoNewlinePlaceholder )
198
- callExpr .Decorations ().End .Append (TrampolineSemicolonPlaceholder )
199
- retStmt := tjump .Body .List [1 ]
200
- retStmt .Decorations ().End .Append (TrampolineNoNewlinePlaceholder )
201
- }
202
- { // else block
203
- deferStmt := tjump .Else .(* dst.BlockStmt ).List [0 ]
204
- deferStmt .Decorations ().Start .Append (TrampolineNoNewlinePlaceholder )
205
- deferStmt .Decorations ().End .Append (TrampolineSemicolonPlaceholder )
206
- tjump .Else .Decorations ().End .Append (TrampolineNoNewlinePlaceholder )
207
- tjump .Decs .If .Append (TrampolineJumpIfDesc ) // Anchor label
208
- }
209
-
182
+ // Add label for trampoline-jump-if. Note that the label will be cleared
183
+ // during optimization pass, to make it pretty in the generated code
184
+ tjump .Decs .If .Append (TJumpLabel )
210
185
// Find if there is already a trampoline-jump-if, insert new tjump if so,
211
186
// otherwise prepend to block body
212
187
found := false
@@ -222,10 +197,35 @@ func (rp *RuleProcessor) insertTJump(t *resource.InstFuncRule,
222
197
}
223
198
}
224
199
if ! found {
225
- // Outmost trampoline-jump-if may follow by user code right after else
226
- // block, replacing the trailing newline mandatorily breaks the code,
227
- // we need to insert extra new line to make replacement possible
228
- tjump .Decorations ().After = dst .EmptyLine
200
+ // Tag the trampoline-jump-if with a special line directive so that
201
+ // debugger can show the correct line number
202
+ tjump .Decs .Before = dst .NewLine
203
+ tjump .Decs .Start .Append ("//line <generated>:1" )
204
+ pos := rp .parser .FindPosition (funcDecl .Body )
205
+ if len (funcDecl .Body .List ) > 0 {
206
+ // It does happens because we may insert raw code snippets at the
207
+ // function entry. These dynamically generated nodes do not have
208
+ // corresponding node positions. We need to keep looking downward
209
+ // until we find a node that contains position information, and then
210
+ // annotate it with a line directive.
211
+ for i := 0 ; i < len (funcDecl .Body .List ); i ++ {
212
+ stmt := funcDecl .Body .List [i ]
213
+ pos = rp .parser .FindPosition (stmt )
214
+ if ! pos .IsValid () {
215
+ continue
216
+ }
217
+ tag := fmt .Sprintf ("//line %s" , pos .String ())
218
+ stmt .Decorations ().Before = dst .NewLine
219
+ stmt .Decorations ().Start .Append (tag )
220
+ }
221
+ } else {
222
+ pos = rp .parser .FindPosition (funcDecl .Body )
223
+ tag := fmt .Sprintf ("//line %s" , pos .String ())
224
+ empty := util .EmptyStmt ()
225
+ empty .Decs .Before = dst .NewLine
226
+ empty .Decs .Start .Append (tag )
227
+ funcDecl .Body .List = append (funcDecl .Body .List , empty )
228
+ }
229
229
funcDecl .Body .List = append ([]dst.Stmt {tjump }, funcDecl .Body .List ... )
230
230
}
231
231
@@ -237,38 +237,21 @@ func (rp *RuleProcessor) insertTJump(t *resource.InstFuncRule,
237
237
return nil
238
238
}
239
239
240
- func (rp * RuleProcessor ) inliningTJump (filePath string ) error {
241
- text , err := util .ReadFile (filePath )
242
- if err != nil {
243
- return err
244
- }
245
- // Remove trailing newline
246
- re := regexp .MustCompile (TrampolineNoNewlinePlaceholderRegexp )
247
- text = re .ReplaceAllString (text , " " )
248
- // Replace with semicolon
249
- re = regexp .MustCompile (TrampolineSemicolonPlaceholderRegexp )
250
- text = re .ReplaceAllString (text , ";" )
251
- // Remove trampoline jump if ideitifiers
252
- re = regexp .MustCompile (TrampolineJumpIfDescRegexp )
253
- text = re .ReplaceAllString (text , "" )
254
- // All done, persist to file
255
- _ , err = util .WriteFile (filePath , text )
256
- return err
257
- }
258
-
259
240
func (rp * RuleProcessor ) insertRaw (r * resource.InstFuncRule , decl * dst.FuncDecl ) error {
260
241
util .Assert (r .OnEnter != "" || r .OnExit != "" , "sanity check" )
261
242
if r .OnEnter != "" {
262
243
// Prepend raw code snippet to function body for onEnter
263
- onEnterSnippet , err := util .ParseAstFromSnippet (r .OnEnter )
244
+ p := util .NewAstParser ()
245
+ onEnterSnippet , err := p .ParseSnippet (r .OnEnter )
264
246
if err != nil {
265
247
return err
266
248
}
267
249
decl .Body .List = append (onEnterSnippet , decl .Body .List ... )
268
250
}
269
251
if r .OnExit != "" {
270
252
// Use defer func(){ raw_code_snippet }() for onExit
271
- onExitSnippet , err := util .ParseAstFromSnippet (
253
+ p := util .NewAstParser ()
254
+ onExitSnippet , err := p .ParseSnippet (
272
255
fmt .Sprintf ("defer func(){ %s }()" , r .OnExit ),
273
256
)
274
257
if err != nil {
@@ -301,8 +284,8 @@ func sortFuncRules(fnRules []*resource.InstFuncRule) []*resource.InstFuncRule {
301
284
302
285
func (rp * RuleProcessor ) writeTrampoline (pkgName string ) error {
303
286
// Prepare trampoline code header
304
- code := "package " + pkgName
305
- trampoline , err := util . ParseAstFromSource ( code )
287
+ p := util . NewAstParser ()
288
+ trampoline , err := p . ParseSource ( "package " + pkgName )
306
289
if err != nil {
307
290
return err
308
291
}
@@ -319,6 +302,18 @@ func (rp *RuleProcessor) writeTrampoline(pkgName string) error {
319
302
return nil
320
303
}
321
304
305
+ func (rp * RuleProcessor ) enableLineDirective (filePath string ) error {
306
+ text , err := util .ReadFile (filePath )
307
+ if err != nil {
308
+ return err
309
+ }
310
+ re := regexp .MustCompile (".*//line " )
311
+ text = re .ReplaceAllString (text , "//line " )
312
+ // All done, persist to file
313
+ _ , err = util .WriteFile (filePath , text )
314
+ return err
315
+ }
316
+
322
317
func (rp * RuleProcessor ) applyFuncRules (bundle * resource.RuleBundle ) (err error ) {
323
318
// Nothing to do if no func rules
324
319
if len (bundle .File2FuncRules ) == 0 {
@@ -338,7 +333,6 @@ func (rp *RuleProcessor) applyFuncRules(bundle *resource.RuleBundle) (err error)
338
333
if err != nil {
339
334
return err
340
335
}
341
- rp .target = astRoot
342
336
rp .trampolineJumps = make ([]* TJump , 0 )
343
337
for fnName , rules := range fn2rules {
344
338
for _ , decl := range astRoot .Decls {
@@ -378,9 +372,9 @@ func (rp *RuleProcessor) applyFuncRules(bundle *resource.RuleBundle) (err error)
378
372
if err != nil {
379
373
return err
380
374
}
381
- // Wait, all above code snippets should be inlined to new line to
382
- // avoid potential misbehaviors during debugging
383
- err = rp .inliningTJump (filePath )
375
+ // Line directive must be placed at the beginning of the line, otherwise
376
+ // it will be ignored by the compiler
377
+ err = rp .enableLineDirective (filePath )
384
378
if err != nil {
385
379
return err
386
380
}
0 commit comments