package profile
import (
"bufio"
"bytes"
"fmt"
"internal/lazyregexp"
"io"
"math"
"strconv"
"strings"
)
var (
countStartRE = lazyregexp .New (`\A(\w+) profile: total \d+\n\z` )
countRE = lazyregexp .New (`\A(\d+) @(( 0x[0-9a-f]+)+)\n\z` )
heapHeaderRE = lazyregexp .New (`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] *@ *(heap[_a-z0-9]*)/?(\d*)` )
heapSampleRE = lazyregexp .New (`(-?\d+): *(-?\d+) *\[ *(\d+): *(\d+) *] @([ x0-9a-f]*)` )
contentionSampleRE = lazyregexp .New (`(\d+) *(\d+) @([ x0-9a-f]*)` )
hexNumberRE = lazyregexp .New (`0x[0-9a-f]+` )
growthHeaderRE = lazyregexp .New (`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] @ growthz` )
fragmentationHeaderRE = lazyregexp .New (`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] @ fragmentationz` )
threadzStartRE = lazyregexp .New (`--- threadz \d+ ---` )
threadStartRE = lazyregexp .New (`--- Thread ([[:xdigit:]]+) \(name: (.*)/(\d+)\) stack: ---` )
procMapsRE = lazyregexp .New (`([[:xdigit:]]+)-([[:xdigit:]]+)\s+([-rwxp]+)\s+([[:xdigit:]]+)\s+([[:xdigit:]]+):([[:xdigit:]]+)\s+([[:digit:]]+)\s*(\S+)?` )
briefMapsRE = lazyregexp .New (`\s*([[:xdigit:]]+)-([[:xdigit:]]+):\s*(\S+)(\s.*@)?([[:xdigit:]]+)?` )
LegacyHeapAllocated bool
)
func isSpaceOrComment(line string ) bool {
trimmed := strings .TrimSpace (line )
return len (trimmed ) == 0 || trimmed [0 ] == '#'
}
func parseGoCount(b []byte ) (*Profile , error ) {
r := bytes .NewBuffer (b )
var line string
var err error
for {
line , err = r .ReadString ('\n' )
if err != nil {
return nil , err
}
if !isSpaceOrComment (line ) {
break
}
}
m := countStartRE .FindStringSubmatch (line )
if m == nil {
return nil , errUnrecognized
}
profileType := m [1 ]
p := &Profile {
PeriodType : &ValueType {Type : profileType , Unit : "count" },
Period : 1 ,
SampleType : []*ValueType {{Type : profileType , Unit : "count" }},
}
locations := make (map [uint64 ]*Location )
for {
line , err = r .ReadString ('\n' )
if err != nil {
if err == io .EOF {
break
}
return nil , err
}
if isSpaceOrComment (line ) {
continue
}
if strings .HasPrefix (line , "---" ) {
break
}
m := countRE .FindStringSubmatch (line )
if m == nil {
return nil , errMalformed
}
n , err := strconv .ParseInt (m [1 ], 0 , 64 )
if err != nil {
return nil , errMalformed
}
fields := strings .Fields (m [2 ])
locs := make ([]*Location , 0 , len (fields ))
for _ , stk := range fields {
addr , err := strconv .ParseUint (stk , 0 , 64 )
if err != nil {
return nil , errMalformed
}
addr --
loc := locations [addr ]
if loc == nil {
loc = &Location {
Address : addr ,
}
locations [addr ] = loc
p .Location = append (p .Location , loc )
}
locs = append (locs , loc )
}
p .Sample = append (p .Sample , &Sample {
Location : locs ,
Value : []int64 {n },
})
}
if err = parseAdditionalSections (strings .TrimSpace (line ), r , p ); err != nil {
return nil , err
}
return p , nil
}
func (p *Profile ) remapLocationIDs () {
seen := make (map [*Location ]bool , len (p .Location ))
var locs []*Location
for _ , s := range p .Sample {
for _ , l := range s .Location {
if seen [l ] {
continue
}
l .ID = uint64 (len (locs ) + 1 )
locs = append (locs , l )
seen [l ] = true
}
}
p .Location = locs
}
func (p *Profile ) remapFunctionIDs () {
seen := make (map [*Function ]bool , len (p .Function ))
var fns []*Function
for _ , l := range p .Location {
for _ , ln := range l .Line {
fn := ln .Function
if fn == nil || seen [fn ] {
continue
}
fn .ID = uint64 (len (fns ) + 1 )
fns = append (fns , fn )
seen [fn ] = true
}
}
p .Function = fns
}
func (p *Profile ) remapMappingIDs () {
if len (p .Mapping ) == 0 {
return
}
if m := p .Mapping [0 ]; strings .HasPrefix (m .File , "/anon_hugepage" ) {
if len (p .Mapping ) > 1 && m .Limit == p .Mapping [1 ].Start {
p .Mapping = p .Mapping [1 :]
}
}
for _ , l := range p .Location {
if a := l .Address ; a != 0 {
for _ , m := range p .Mapping {
if m .Start <= a && a < m .Limit {
l .Mapping = m
break
}
}
}
}
for i , m := range p .Mapping {
m .ID = uint64 (i + 1 )
}
}
var cpuInts = []func ([]byte ) (uint64 , []byte ){
get32l ,
get32b ,
get64l ,
get64b ,
}
func get32l(b []byte ) (uint64 , []byte ) {
if len (b ) < 4 {
return 0 , nil
}
return uint64 (b [0 ]) | uint64 (b [1 ])<<8 | uint64 (b [2 ])<<16 | uint64 (b [3 ])<<24 , b [4 :]
}
func get32b(b []byte ) (uint64 , []byte ) {
if len (b ) < 4 {
return 0 , nil
}
return uint64 (b [3 ]) | uint64 (b [2 ])<<8 | uint64 (b [1 ])<<16 | uint64 (b [0 ])<<24 , b [4 :]
}
func get64l(b []byte ) (uint64 , []byte ) {
if len (b ) < 8 {
return 0 , nil
}
return uint64 (b [0 ]) | uint64 (b [1 ])<<8 | uint64 (b [2 ])<<16 | uint64 (b [3 ])<<24 | uint64 (b [4 ])<<32 | uint64 (b [5 ])<<40 | uint64 (b [6 ])<<48 | uint64 (b [7 ])<<56 , b [8 :]
}
func get64b(b []byte ) (uint64 , []byte ) {
if len (b ) < 8 {
return 0 , nil
}
return uint64 (b [7 ]) | uint64 (b [6 ])<<8 | uint64 (b [5 ])<<16 | uint64 (b [4 ])<<24 | uint64 (b [3 ])<<32 | uint64 (b [2 ])<<40 | uint64 (b [1 ])<<48 | uint64 (b [0 ])<<56 , b [8 :]
}
func ParseTracebacks (b []byte ) (*Profile , error ) {
r := bytes .NewBuffer (b )
p := &Profile {
PeriodType : &ValueType {Type : "trace" , Unit : "count" },
Period : 1 ,
SampleType : []*ValueType {
{Type : "trace" , Unit : "count" },
},
}
var sources []string
var sloc []*Location
locs := make (map [uint64 ]*Location )
for {
l , err := r .ReadString ('\n' )
if err != nil {
if err != io .EOF {
return nil , err
}
if l == "" {
break
}
}
if sectionTrigger (l ) == memoryMapSection {
break
}
if s , addrs := extractHexAddresses (l ); len (s ) > 0 {
for _ , addr := range addrs {
addr --
loc := locs [addr ]
if locs [addr ] == nil {
loc = &Location {
Address : addr ,
}
p .Location = append (p .Location , loc )
locs [addr ] = loc
}
sloc = append (sloc , loc )
}
sources = append (sources , s ...)
} else {
if len (sources ) > 0 || len (sloc ) > 0 {
addTracebackSample (sloc , sources , p )
sloc , sources = nil , nil
}
}
}
if len (sources ) > 0 || len (sloc ) > 0 {
addTracebackSample (sloc , sources , p )
}
if err := p .ParseMemoryMap (r ); err != nil {
return nil , err
}
return p , nil
}
func addTracebackSample(l []*Location , s []string , p *Profile ) {
p .Sample = append (p .Sample ,
&Sample {
Value : []int64 {1 },
Location : l ,
Label : map [string ][]string {"source" : s },
})
}
func parseCPU(b []byte ) (*Profile , error ) {
var parse func ([]byte ) (uint64 , []byte )
var n1 , n2 , n3 , n4 , n5 uint64
for _, parse = range cpuInts {
var tmp []byte
n1 , tmp = parse (b )
n2 , tmp = parse (tmp )
n3 , tmp = parse (tmp )
n4 , tmp = parse (tmp )
n5 , tmp = parse (tmp )
if tmp != nil && n1 == 0 && n2 == 3 && n3 == 0 && n4 > 0 && n5 == 0 {
b = tmp
return cpuProfile (b , int64 (n4 ), parse )
}
}
return nil , errUnrecognized
}
func cpuProfile(b []byte , period int64 , parse func (b []byte ) (uint64 , []byte )) (*Profile , error ) {
p := &Profile {
Period : period * 1000 ,
PeriodType : &ValueType {Type : "cpu" , Unit : "nanoseconds" },
SampleType : []*ValueType {
{Type : "samples" , Unit : "count" },
{Type : "cpu" , Unit : "nanoseconds" },
},
}
var err error
if b , _, err = parseCPUSamples (b , parse , true , p ); err != nil {
return nil , err
}
if len (p .Sample ) > 1 && len (p .Sample [0 ].Location ) > 1 {
allSame := true
id1 := p .Sample [0 ].Location [1 ].Address
for _ , s := range p .Sample {
if len (s .Location ) < 2 || id1 != s .Location [1 ].Address {
allSame = false
break
}
}
if allSame {
for _ , s := range p .Sample {
s .Location = append (s .Location [:1 ], s .Location [2 :]...)
}
}
}
if err := p .ParseMemoryMap (bytes .NewBuffer (b )); err != nil {
return nil , err
}
return p , nil
}
func parseCPUSamples(b []byte , parse func (b []byte ) (uint64 , []byte ), adjust bool , p *Profile ) ([]byte , map [uint64 ]*Location , error ) {
locs := make (map [uint64 ]*Location )
for len (b ) > 0 {
var count , nstk uint64
count , b = parse (b )
nstk , b = parse (b )
if b == nil || nstk > uint64 (len (b )/4 ) {
return nil , nil , errUnrecognized
}
var sloc []*Location
addrs := make ([]uint64 , nstk )
for i := 0 ; i < int (nstk ); i ++ {
addrs [i ], b = parse (b )
}
if count == 0 && nstk == 1 && addrs [0 ] == 0 {
break
}
for i , addr := range addrs {
if adjust && i > 0 {
addr --
}
loc := locs [addr ]
if loc == nil {
loc = &Location {
Address : addr ,
}
locs [addr ] = loc
p .Location = append (p .Location , loc )
}
sloc = append (sloc , loc )
}
p .Sample = append (p .Sample ,
&Sample {
Value : []int64 {int64 (count ), int64 (count ) * p .Period },
Location : sloc ,
})
}
return b , locs , nil
}
func parseHeap(b []byte ) (p *Profile , err error ) {
r := bytes .NewBuffer (b )
l , err := r .ReadString ('\n' )
if err != nil {
return nil , errUnrecognized
}
sampling := ""
if header := heapHeaderRE .FindStringSubmatch (l ); header != nil {
p = &Profile {
SampleType : []*ValueType {
{Type : "objects" , Unit : "count" },
{Type : "space" , Unit : "bytes" },
},
PeriodType : &ValueType {Type : "objects" , Unit : "bytes" },
}
var period int64
if len (header [6 ]) > 0 {
if period , err = strconv .ParseInt (header [6 ], 10 , 64 ); err != nil {
return nil , errUnrecognized
}
}
switch header [5 ] {
case "heapz_v2" , "heap_v2" :
sampling , p .Period = "v2" , period
case "heapprofile" :
sampling , p .Period = "" , 1
case "heap" :
sampling , p .Period = "v2" , period /2
default :
return nil , errUnrecognized
}
} else if header = growthHeaderRE .FindStringSubmatch (l ); header != nil {
p = &Profile {
SampleType : []*ValueType {
{Type : "objects" , Unit : "count" },
{Type : "space" , Unit : "bytes" },
},
PeriodType : &ValueType {Type : "heapgrowth" , Unit : "count" },
Period : 1 ,
}
} else if header = fragmentationHeaderRE .FindStringSubmatch (l ); header != nil {
p = &Profile {
SampleType : []*ValueType {
{Type : "objects" , Unit : "count" },
{Type : "space" , Unit : "bytes" },
},
PeriodType : &ValueType {Type : "allocations" , Unit : "count" },
Period : 1 ,
}
} else {
return nil , errUnrecognized
}
if LegacyHeapAllocated {
for _ , st := range p .SampleType {
st .Type = "alloc_" + st .Type
}
} else {
for _ , st := range p .SampleType {
st .Type = "inuse_" + st .Type
}
}
locs := make (map [uint64 ]*Location )
for {
l , err = r .ReadString ('\n' )
if err != nil {
if err != io .EOF {
return nil , err
}
if l == "" {
break
}
}
if isSpaceOrComment (l ) {
continue
}
l = strings .TrimSpace (l )
if sectionTrigger (l ) != unrecognizedSection {
break
}
value , blocksize , addrs , err := parseHeapSample (l , p .Period , sampling )
if err != nil {
return nil , err
}
var sloc []*Location
for _ , addr := range addrs {
addr --
loc := locs [addr ]
if locs [addr ] == nil {
loc = &Location {
Address : addr ,
}
p .Location = append (p .Location , loc )
locs [addr ] = loc
}
sloc = append (sloc , loc )
}
p .Sample = append (p .Sample , &Sample {
Value : value ,
Location : sloc ,
NumLabel : map [string ][]int64 {"bytes" : {blocksize }},
})
}
if err = parseAdditionalSections (l , r , p ); err != nil {
return nil , err
}
return p , nil
}
func parseHeapSample(line string , rate int64 , sampling string ) (value []int64 , blocksize int64 , addrs []uint64 , err error ) {
sampleData := heapSampleRE .FindStringSubmatch (line )
if len (sampleData ) != 6 {
return value , blocksize , addrs , fmt .Errorf ("unexpected number of sample values: got %d, want 6" , len (sampleData ))
}
valueIndex := 1
if LegacyHeapAllocated {
valueIndex = 3
}
var v1 , v2 int64
if v1 , err = strconv .ParseInt (sampleData [valueIndex ], 10 , 64 ); err != nil {
return value , blocksize , addrs , fmt .Errorf ("malformed sample: %s: %v" , line , err )
}
if v2 , err = strconv .ParseInt (sampleData [valueIndex +1 ], 10 , 64 ); err != nil {
return value , blocksize , addrs , fmt .Errorf ("malformed sample: %s: %v" , line , err )
}
if v1 == 0 {
if v2 != 0 {
return value , blocksize , addrs , fmt .Errorf ("allocation count was 0 but allocation bytes was %d" , v2 )
}
} else {
blocksize = v2 / v1
if sampling == "v2" {
v1 , v2 = scaleHeapSample (v1 , v2 , rate )
}
}
value = []int64 {v1 , v2 }
addrs = parseHexAddresses (sampleData [5 ])
return value , blocksize , addrs , nil
}
func extractHexAddresses(s string ) ([]string , []uint64 ) {
hexStrings := hexNumberRE .FindAllString (s , -1 )
var ids []uint64
for _ , s := range hexStrings {
if id , err := strconv .ParseUint (s , 0 , 64 ); err == nil {
ids = append (ids , id )
} else {
panic ("failed to parse hex value:" + s )
}
}
return hexStrings , ids
}
func parseHexAddresses(s string ) []uint64 {
_ , ids := extractHexAddresses (s )
return ids
}
func scaleHeapSample(count , size , rate int64 ) (int64 , int64 ) {
if count == 0 || size == 0 {
return 0 , 0
}
if rate <= 1 {
return count , size
}
avgSize := float64 (size ) / float64 (count )
scale := 1 / (1 - math .Exp (-avgSize /float64 (rate )))
return int64 (float64 (count ) * scale ), int64 (float64 (size ) * scale )
}
func parseContention(b []byte ) (*Profile , error ) {
r := bytes .NewBuffer (b )
var l string
var err error
for {
l , err = r .ReadString ('\n' )
if err != nil {
return nil , err
}
if !isSpaceOrComment (l ) {
break
}
}
if strings .HasPrefix (l , "--- contentionz " ) {
return parseCppContention (r )
} else if strings .HasPrefix (l , "--- mutex:" ) {
return parseCppContention (r )
} else if strings .HasPrefix (l , "--- contention:" ) {
return parseCppContention (r )
}
return nil , errUnrecognized
}
func parseCppContention(r *bytes .Buffer ) (*Profile , error ) {
p := &Profile {
PeriodType : &ValueType {Type : "contentions" , Unit : "count" },
Period : 1 ,
SampleType : []*ValueType {
{Type : "contentions" , Unit : "count" },
{Type : "delay" , Unit : "nanoseconds" },
},
}
var cpuHz int64
var l string
var err error
const delimiter = '='
for {
l , err = r .ReadString ('\n' )
if err != nil {
if err != io .EOF {
return nil , err
}
if l == "" {
break
}
}
if isSpaceOrComment (l ) {
continue
}
if l = strings .TrimSpace (l ); l == "" {
continue
}
if strings .HasPrefix (l , "---" ) {
break
}
index := strings .IndexByte (l , delimiter )
if index < 0 {
break
}
key := l [:index ]
val := l [index +1 :]
key , val = strings .TrimSpace (key ), strings .TrimSpace (val )
var err error
switch key {
case "cycles/second" :
if cpuHz , err = strconv .ParseInt (val , 0 , 64 ); err != nil {
return nil , errUnrecognized
}
case "sampling period" :
if p .Period , err = strconv .ParseInt (val , 0 , 64 ); err != nil {
return nil , errUnrecognized
}
case "ms since reset" :
ms , err := strconv .ParseInt (val , 0 , 64 )
if err != nil {
return nil , errUnrecognized
}
p .DurationNanos = ms * 1000 * 1000
case "format" :
return nil , errUnrecognized
case "resolution" :
return nil , errUnrecognized
case "discarded samples" :
default :
return nil , errUnrecognized
}
}
locs := make (map [uint64 ]*Location )
for {
if !isSpaceOrComment (l ) {
if l = strings .TrimSpace (l ); strings .HasPrefix (l , "---" ) {
break
}
value , addrs , err := parseContentionSample (l , p .Period , cpuHz )
if err != nil {
return nil , err
}
var sloc []*Location
for _ , addr := range addrs {
addr --
loc := locs [addr ]
if locs [addr ] == nil {
loc = &Location {
Address : addr ,
}
p .Location = append (p .Location , loc )
locs [addr ] = loc
}
sloc = append (sloc , loc )
}
p .Sample = append (p .Sample , &Sample {
Value : value ,
Location : sloc ,
})
}
if l , err = r .ReadString ('\n' ); err != nil {
if err != io .EOF {
return nil , err
}
if l == "" {
break
}
}
}
if err = parseAdditionalSections (l , r , p ); err != nil {
return nil , err
}
return p , nil
}
func parseContentionSample(line string , period , cpuHz int64 ) (value []int64 , addrs []uint64 , err error ) {
sampleData := contentionSampleRE .FindStringSubmatch (line )
if sampleData == nil {
return value , addrs , errUnrecognized
}
v1 , err := strconv .ParseInt (sampleData [1 ], 10 , 64 )
if err != nil {
return value , addrs , fmt .Errorf ("malformed sample: %s: %v" , line , err )
}
v2 , err := strconv .ParseInt (sampleData [2 ], 10 , 64 )
if err != nil {
return value , addrs , fmt .Errorf ("malformed sample: %s: %v" , line , err )
}
if period > 0 {
if cpuHz > 0 {
cpuGHz := float64 (cpuHz ) / 1e9
v1 = int64 (float64 (v1 ) * float64 (period ) / cpuGHz )
}
v2 = v2 * period
}
value = []int64 {v2 , v1 }
addrs = parseHexAddresses (sampleData [3 ])
return value , addrs , nil
}
func parseThread(b []byte ) (*Profile , error ) {
r := bytes .NewBuffer (b )
var line string
var err error
for {
line , err = r .ReadString ('\n' )
if err != nil {
return nil , err
}
if !isSpaceOrComment (line ) {
break
}
}
if m := threadzStartRE .FindStringSubmatch (line ); m != nil {
for {
line , err = r .ReadString ('\n' )
if err != nil {
if err != io .EOF {
return nil , err
}
if line == "" {
break
}
}
if sectionTrigger (line ) != unrecognizedSection || line [0 ] == '-' {
break
}
}
} else if t := threadStartRE .FindStringSubmatch (line ); len (t ) != 4 {
return nil , errUnrecognized
}
p := &Profile {
SampleType : []*ValueType {{Type : "thread" , Unit : "count" }},
PeriodType : &ValueType {Type : "thread" , Unit : "count" },
Period : 1 ,
}
locs := make (map [uint64 ]*Location )
for sectionTrigger (line ) == unrecognizedSection {
if strings .HasPrefix (line , "---- no stack trace for" ) {
line = ""
break
}
if t := threadStartRE .FindStringSubmatch (line ); len (t ) != 4 {
return nil , errUnrecognized
}
var addrs []uint64
line , addrs , err = parseThreadSample (r )
if err != nil {
return nil , errUnrecognized
}
if len (addrs ) == 0 {
if len (p .Sample ) > 0 {
s := p .Sample [len (p .Sample )-1 ]
s .Value [0 ]++
}
continue
}
var sloc []*Location
for _ , addr := range addrs {
addr --
loc := locs [addr ]
if locs [addr ] == nil {
loc = &Location {
Address : addr ,
}
p .Location = append (p .Location , loc )
locs [addr ] = loc
}
sloc = append (sloc , loc )
}
p .Sample = append (p .Sample , &Sample {
Value : []int64 {1 },
Location : sloc ,
})
}
if err = parseAdditionalSections (line , r , p ); err != nil {
return nil , err
}
return p , nil
}
func parseThreadSample(b *bytes .Buffer ) (nextl string , addrs []uint64 , err error ) {
var l string
sameAsPrevious := false
for {
if l , err = b .ReadString ('\n' ); err != nil {
if err != io .EOF {
return "" , nil , err
}
if l == "" {
break
}
}
if l = strings .TrimSpace (l ); l == "" {
continue
}
if strings .HasPrefix (l , "---" ) {
break
}
if strings .Contains (l , "same as previous thread" ) {
sameAsPrevious = true
continue
}
addrs = append (addrs , parseHexAddresses (l )...)
}
if sameAsPrevious {
return l , nil , nil
}
return l , addrs , nil
}
func parseAdditionalSections(l string , b *bytes .Buffer , p *Profile ) (err error ) {
for {
if sectionTrigger (l ) == memoryMapSection {
break
}
if l , err := b .ReadString ('\n' ); err != nil {
if err != io .EOF {
return err
}
if l == "" {
break
}
}
}
return p .ParseMemoryMap (b )
}
func (p *Profile ) ParseMemoryMap (rd io .Reader ) error {
b := bufio .NewReader (rd )
var attrs []string
var r *strings .Replacer
const delimiter = '='
for {
l , err := b .ReadString ('\n' )
if err != nil {
if err != io .EOF {
return err
}
if l == "" {
break
}
}
if l = strings .TrimSpace (l ); l == "" {
continue
}
if r != nil {
l = r .Replace (l )
}
m , err := parseMappingEntry (l )
if err != nil {
if err == errUnrecognized {
idx := strings .IndexByte (l , delimiter )
if idx >= 0 {
attr := l [:idx ]
value := l [idx +1 :]
attrs = append (attrs , "$" +strings .TrimSpace (attr ), strings .TrimSpace (value ))
r = strings .NewReplacer (attrs ...)
}
continue
}
return err
}
if m == nil || (m .File == "" && len (p .Mapping ) != 0 ) {
continue
}
if len (p .Mapping ) == 1 && p .Mapping [0 ].File == "" {
p .Mapping [0 ].File = m .File
continue
}
p .Mapping = append (p .Mapping , m )
}
p .remapLocationIDs ()
p .remapFunctionIDs ()
p .remapMappingIDs ()
return nil
}
func parseMappingEntry(l string ) (*Mapping , error ) {
mapping := &Mapping {}
var err error
if me := procMapsRE .FindStringSubmatch (l ); len (me ) == 9 {
if !strings .Contains (me [3 ], "x" ) {
return nil , nil
}
if mapping .Start , err = strconv .ParseUint (me [1 ], 16 , 64 ); err != nil {
return nil , errUnrecognized
}
if mapping .Limit , err = strconv .ParseUint (me [2 ], 16 , 64 ); err != nil {
return nil , errUnrecognized
}
if me [4 ] != "" {
if mapping .Offset , err = strconv .ParseUint (me [4 ], 16 , 64 ); err != nil {
return nil , errUnrecognized
}
}
mapping .File = me [8 ]
return mapping , nil
}
if me := briefMapsRE .FindStringSubmatch (l ); len (me ) == 6 {
if mapping .Start , err = strconv .ParseUint (me [1 ], 16 , 64 ); err != nil {
return nil , errUnrecognized
}
if mapping .Limit , err = strconv .ParseUint (me [2 ], 16 , 64 ); err != nil {
return nil , errUnrecognized
}
mapping .File = me [3 ]
if me [5 ] != "" {
if mapping .Offset , err = strconv .ParseUint (me [5 ], 16 , 64 ); err != nil {
return nil , errUnrecognized
}
}
return mapping , nil
}
return nil , errUnrecognized
}
type sectionType int
const (
unrecognizedSection sectionType = iota
memoryMapSection
)
var memoryMapTriggers = []string {
"--- Memory map: ---" ,
"MAPPED_LIBRARIES:" ,
}
func sectionTrigger(line string ) sectionType {
for _ , trigger := range memoryMapTriggers {
if strings .Contains (line , trigger ) {
return memoryMapSection
}
}
return unrecognizedSection
}
func (p *Profile ) addLegacyFrameInfo () {
switch {
case isProfileType (p , heapzSampleTypes ) ||
isProfileType (p , heapzInUseSampleTypes ) ||
isProfileType (p , heapzAllocSampleTypes ):
p .DropFrames , p .KeepFrames = allocRxStr , allocSkipRxStr
case isProfileType (p , contentionzSampleTypes ):
p .DropFrames , p .KeepFrames = lockRxStr , ""
default :
p .DropFrames , p .KeepFrames = cpuProfilerRxStr , ""
}
}
var heapzSampleTypes = []string {"allocations" , "size" }
var heapzInUseSampleTypes = []string {"inuse_objects" , "inuse_space" }
var heapzAllocSampleTypes = []string {"alloc_objects" , "alloc_space" }
var contentionzSampleTypes = []string {"contentions" , "delay" }
func isProfileType(p *Profile , t []string ) bool {
st := p .SampleType
if len (st ) != len (t ) {
return false
}
for i := range st {
if st [i ].Type != t [i ] {
return false
}
}
return true
}
var allocRxStr = strings .Join ([]string {
`calloc` ,
`cfree` ,
`malloc` ,
`free` ,
`memalign` ,
`do_memalign` ,
`(__)?posix_memalign` ,
`pvalloc` ,
`valloc` ,
`realloc` ,
`tcmalloc::.*` ,
`tc_calloc` ,
`tc_cfree` ,
`tc_malloc` ,
`tc_free` ,
`tc_memalign` ,
`tc_posix_memalign` ,
`tc_pvalloc` ,
`tc_valloc` ,
`tc_realloc` ,
`tc_new` ,
`tc_delete` ,
`tc_newarray` ,
`tc_deletearray` ,
`tc_new_nothrow` ,
`tc_newarray_nothrow` ,
`malloc_zone_malloc` ,
`malloc_zone_calloc` ,
`malloc_zone_valloc` ,
`malloc_zone_realloc` ,
`malloc_zone_memalign` ,
`malloc_zone_free` ,
`runtime\..*` ,
`BaseArena::.*` ,
`(::)?do_malloc_no_errno` ,
`(::)?do_malloc_pages` ,
`(::)?do_malloc` ,
`DoSampledAllocation` ,
`MallocedMemBlock::MallocedMemBlock` ,
`_M_allocate` ,
`__builtin_(vec_)?delete` ,
`__builtin_(vec_)?new` ,
`__gnu_cxx::new_allocator::allocate` ,
`__libc_malloc` ,
`__malloc_alloc_template::allocate` ,
`allocate` ,
`cpp_alloc` ,
`operator new(\[\])?` ,
`simple_alloc::allocate` ,
}, `|` )
var allocSkipRxStr = strings .Join ([]string {
`runtime\.panic` ,
`runtime\.reflectcall` ,
`runtime\.call[0-9]*` ,
}, `|` )
var cpuProfilerRxStr = strings .Join ([]string {
`ProfileData::Add` ,
`ProfileData::prof_handler` ,
`CpuProfiler::prof_handler` ,
`__pthread_sighandler` ,
`__restore` ,
}, `|` )
var lockRxStr = strings .Join ([]string {
`RecordLockProfileData` ,
`(base::)?RecordLockProfileData.*` ,
`(base::)?SubmitMutexProfileData.*` ,
`(base::)?SubmitSpinLockProfileData.*` ,
`(Mutex::)?AwaitCommon.*` ,
`(Mutex::)?Unlock.*` ,
`(Mutex::)?UnlockSlow.*` ,
`(Mutex::)?ReaderUnlock.*` ,
`(MutexLock::)?~MutexLock.*` ,
`(SpinLock::)?Unlock.*` ,
`(SpinLock::)?SlowUnlock.*` ,
`(SpinLockHolder::)?~SpinLockHolder.*` ,
}, `|` )
The pages are generated with Golds v0.6.9-preview . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .