diff --git a/CHANGELOG.md b/CHANGELOG.md index dbbb18f..ec463d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ **Features**: - Add support for speedscope rendering of Android reactnative profiles ([#386](https://github.com/getsentry/vroom/pull/386)) +- Add callTree generation for reactnative (android+js) profiles ([#390](https://github.com/getsentry/vroom/pull/390)) **Bug Fixes**: diff --git a/internal/profile/legacy.go b/internal/profile/legacy.go index 1849192..f0a965a 100644 --- a/internal/profile/legacy.go +++ b/internal/profile/legacy.go @@ -24,6 +24,7 @@ import ( const maxProfileDurationForCallTrees = 15 * time.Second var ErrProfileHasNoTrace = errors.New("profile has no trace") +var ErrReactHasInvalidJsTrace = errors.New("react-android profile has invalid js trace") var member void type ( @@ -146,6 +147,26 @@ func (p LegacyProfile) CallTrees() (map[uint64][]*nodetree.Node, error) { if p.Trace == nil { return nil, ErrProfileHasNoTrace } + _, ok := p.Trace.(Android) + // this is to handle only the Reactnative (android + js) + // use case. If it's an Android profile but there is no + // js profile, we'll skip this entirely + if ok && p.JsProfile != nil && len(p.JsProfile) > 0 { + st, err := unmarshalSampleProfile(p.JsProfile) + if err != nil { + return nil, ErrReactHasInvalidJsTrace + } + jsProf := sample.Profile{ + RawProfile: sample.RawProfile{ + Trace: st, + }, + } + err = fillSampleProfileMetadata(&jsProf) + if err != nil { + return nil, err + } + return jsProf.CallTrees() + } return p.Trace.CallTrees(), nil } @@ -472,3 +493,21 @@ func unmarshalSampleProfile(p json.RawMessage) (sample.Trace, error) { return st, nil } + +// CallTree generation expect activeThreadID to be set in +// order to be able to choose which samples should be used +// for the aggregation. +// Here we set it to the only thread that the js profile has. +func fillSampleProfileMetadata(sp *sample.Profile) error { + for threadID := range sp.Trace.ThreadMetadata { + tid, err := strconv.ParseUint(threadID, 10, 64) + if err != nil { + return err + } + sp.Transaction = transaction.Transaction{ + ActiveThreadID: tid, + } + break + } + return nil +}