@@ -341,6 +341,7 @@ type symbolInfo struct {
341
341
}
342
342
343
343
type pcRange struct {
344
+ // [start; end)
344
345
start uint64
345
346
end uint64
346
347
unit * CompileUnit
@@ -351,7 +352,32 @@ type pcFixFn = (func([2]uint64) ([2]uint64, bool))
351
352
func readTextRanges (debugInfo * dwarf.Data , module * vminfo.KernelModule , pcFix pcFixFn ) (
352
353
[]pcRange , []* CompileUnit , error ) {
353
354
var ranges []pcRange
354
- var units []* CompileUnit
355
+ unitMap := map [string ]* CompileUnit {}
356
+ addRange := func (r [2 ]uint64 , fileName string ) {
357
+ if pcFix != nil {
358
+ var filtered bool
359
+ r , filtered = pcFix (r )
360
+ if filtered {
361
+ return
362
+ }
363
+ }
364
+ unit , ok := unitMap [fileName ]
365
+ if ! ok {
366
+ unit = & CompileUnit {
367
+ ObjectUnit : ObjectUnit {
368
+ Name : fileName ,
369
+ },
370
+ Module : module ,
371
+ }
372
+ unitMap [fileName ] = unit
373
+ }
374
+ if module .Name == "" {
375
+ ranges = append (ranges , pcRange {r [0 ], r [1 ], unit })
376
+ } else {
377
+ ranges = append (ranges , pcRange {r [0 ] + module .Addr , r [1 ] + module .Addr , unit })
378
+ }
379
+ }
380
+
355
381
for r := debugInfo .Reader (); ; {
356
382
ent , err := r .Next ()
357
383
if err != nil {
@@ -363,41 +389,99 @@ func readTextRanges(debugInfo *dwarf.Data, module *vminfo.KernelModule, pcFix pc
363
389
if ent .Tag != dwarf .TagCompileUnit {
364
390
return nil , nil , fmt .Errorf ("found unexpected tag %v on top level" , ent .Tag )
365
391
}
366
- attrName := ent .Val (dwarf .AttrName )
367
- if attrName == nil {
392
+ attrName , ok := ent .Val (dwarf .AttrName ).( string )
393
+ if ! ok {
368
394
continue
369
395
}
370
- unit := & CompileUnit {
371
- ObjectUnit : ObjectUnit {
372
- Name : attrName .(string ),
373
- },
374
- Module : module ,
375
- }
376
- units = append (units , unit )
377
- ranges1 , err := debugInfo .Ranges (ent )
378
- if err != nil {
379
- return nil , nil , err
380
- }
396
+ attrCompDir , _ := ent .Val (dwarf .AttrCompDir ).(string )
381
397
382
- var filtered bool
383
- for _ , r := range ranges1 {
384
- if pcFix != nil {
385
- r , filtered = pcFix (r )
386
- if filtered {
387
- continue
388
- }
398
+ const languageRust = 28
399
+
400
+ language := ent .Val (dwarf .AttrLanguage )
401
+ if language != nil && language .(int64 ) == languageRust {
402
+ rawRanges , err := rustRanges (debugInfo , ent )
403
+ if err != nil {
404
+ return nil , nil , fmt .Errorf ("failed to query Rust PC ranges: %w" , err )
405
+ }
406
+ for _ , r := range rawRanges {
407
+ addRange ([2 ]uint64 {r .start , r .end }, r .file )
389
408
}
390
- if module .Name == "" {
391
- ranges = append (ranges , pcRange {r [0 ], r [1 ], unit })
392
- } else {
393
- ranges = append (ranges , pcRange {r [0 ] + module .Addr , r [1 ] + module .Addr , unit })
409
+ } else {
410
+ // Compile unit names are relative to the compilation dir,
411
+ // while per-line info isn't.
412
+ // Let's stick to the common approach.
413
+ unitName := filepath .Join (attrCompDir , attrName )
414
+ ranges1 , err := debugInfo .Ranges (ent )
415
+ if err != nil {
416
+ return nil , nil , err
417
+ }
418
+ for _ , r := range ranges1 {
419
+ addRange (r , unitName )
394
420
}
395
421
}
396
422
r .SkipChildren ()
397
423
}
424
+ var units []* CompileUnit
425
+ for _ , unit := range unitMap {
426
+ units = append (units , unit )
427
+ }
398
428
return ranges , units , nil
399
429
}
400
430
431
+ type rustRange struct {
432
+ // [start; end)
433
+ start uint64
434
+ end uint64
435
+ file string
436
+ }
437
+
438
+ func rustRanges (debugInfo * dwarf.Data , ent * dwarf.Entry ) ([]rustRange , error ) {
439
+ // For Rust, a single compilation unit may comprise all .rs files that belong to the crate.
440
+ // To properly render the coverage, we need to somehow infer the ranges that belong to
441
+ // those individual .rs files.
442
+ // For simplicity, let's create fake ranges by looking at the DWARF line information.
443
+ var ret []rustRange
444
+ lr , err := debugInfo .LineReader (ent )
445
+ if err != nil {
446
+ return nil , fmt .Errorf ("failed to query line reader: %w" , err )
447
+ }
448
+ var startPC uint64
449
+ var files []string
450
+ for {
451
+ var entry dwarf.LineEntry
452
+ if err = lr .Next (& entry ); err != nil {
453
+ if err == io .EOF {
454
+ break
455
+ }
456
+ return nil , fmt .Errorf ("failed to parse next line entry: %w" , err )
457
+ }
458
+ if startPC == 0 || entry .Address != startPC {
459
+ for _ , file := range files {
460
+ ret = append (ret , rustRange {
461
+ start : startPC ,
462
+ end : entry .Address ,
463
+ file : file ,
464
+ })
465
+ }
466
+ files = files [:0 ]
467
+ startPC = entry .Address
468
+ }
469
+ // Keep on collecting file names that are covered by the range.
470
+ files = append (files , entry .File .Name )
471
+ }
472
+ if startPC != 0 {
473
+ // We don't know the end PC for these, but let's still add them to the ranges.
474
+ for _ , file := range files {
475
+ ret = append (ret , rustRange {
476
+ start : startPC ,
477
+ end : startPC + 1 ,
478
+ file : file ,
479
+ })
480
+ }
481
+ }
482
+ return ret , nil
483
+ }
484
+
401
485
func symbolizeModule (target * targets.Target , interner * symbolizer.Interner , kernelDirs * mgrconfig.KernelDirs ,
402
486
splitBuildDelimiters []string , mod * vminfo.KernelModule , pcs []uint64 ) ([]* Frame , error ) {
403
487
procs := min (runtime .GOMAXPROCS (0 )/ 2 , len (pcs )/ 1000 )
0 commit comments