| package driver |
| |
| import ( |
| "strings" |
| |
| "github.com/google/pprof/internal/measurement" |
| "github.com/google/pprof/profile" |
| ) |
| |
| // addLabelNodes adds pseudo stack frames "label:value" to each Sample with |
| // labels matching the supplied keys. |
| // |
| // rootKeys adds frames at the root of the callgraph (first key becomes new root). |
| // leafKeys adds frames at the leaf of the callgraph (last key becomes new leaf). |
| // |
| // Returns whether there were matches found for the label keys. |
| func addLabelNodes(p *profile.Profile, rootKeys, leafKeys []string, outputUnit string) (rootm, leafm bool) { |
| // Find where to insert the new locations and functions at the end of |
| // their ID spaces. |
| var maxLocID uint64 |
| var maxFunctionID uint64 |
| for _, loc := range p.Location { |
| if loc.ID > maxLocID { |
| maxLocID = loc.ID |
| } |
| } |
| for _, f := range p.Function { |
| if f.ID > maxFunctionID { |
| maxFunctionID = f.ID |
| } |
| } |
| nextLocID := maxLocID + 1 |
| nextFuncID := maxFunctionID + 1 |
| |
| // Intern the new locations and functions we are generating. |
| type locKey struct { |
| functionName, fileName string |
| } |
| locs := map[locKey]*profile.Location{} |
| |
| internLoc := func(locKey locKey) *profile.Location { |
| loc, found := locs[locKey] |
| if found { |
| return loc |
| } |
| |
| function := &profile.Function{ |
| ID: nextFuncID, |
| Name: locKey.functionName, |
| Filename: locKey.fileName, |
| } |
| nextFuncID++ |
| p.Function = append(p.Function, function) |
| |
| loc = &profile.Location{ |
| ID: nextLocID, |
| Line: []profile.Line{ |
| { |
| Function: function, |
| }, |
| }, |
| } |
| nextLocID++ |
| p.Location = append(p.Location, loc) |
| locs[locKey] = loc |
| return loc |
| } |
| |
| makeLabelLocs := func(s *profile.Sample, keys []string) ([]*profile.Location, bool) { |
| var locs []*profile.Location |
| var match bool |
| for i := range keys { |
| // Loop backwards, ensuring the first tag is closest to the root, |
| // and the last tag is closest to the leaves. |
| k := keys[len(keys)-1-i] |
| values := formatLabelValues(s, k, outputUnit) |
| if len(values) > 0 { |
| match = true |
| } |
| locKey := locKey{ |
| functionName: strings.Join(values, ","), |
| fileName: k, |
| } |
| loc := internLoc(locKey) |
| locs = append(locs, loc) |
| } |
| return locs, match |
| } |
| |
| for _, s := range p.Sample { |
| rootsToAdd, sampleMatchedRoot := makeLabelLocs(s, rootKeys) |
| if sampleMatchedRoot { |
| rootm = true |
| } |
| leavesToAdd, sampleMatchedLeaf := makeLabelLocs(s, leafKeys) |
| if sampleMatchedLeaf { |
| leafm = true |
| } |
| |
| var newLocs []*profile.Location |
| newLocs = append(newLocs, leavesToAdd...) |
| newLocs = append(newLocs, s.Location...) |
| newLocs = append(newLocs, rootsToAdd...) |
| s.Location = newLocs |
| } |
| return |
| } |
| |
| // formatLabelValues returns all the string and numeric labels in Sample, with |
| // the numeric labels formatted according to outputUnit. |
| func formatLabelValues(s *profile.Sample, k string, outputUnit string) []string { |
| var values []string |
| values = append(values, s.Label[k]...) |
| numLabels := s.NumLabel[k] |
| numUnits := s.NumUnit[k] |
| if len(numLabels) != len(numUnits) { |
| return values |
| } |
| for i, numLabel := range numLabels { |
| unit := numUnits[i] |
| values = append(values, measurement.ScaledLabel(numLabel, unit, outputUnit)) |
| } |
| return values |
| } |