Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add -diff flag for better profile comparision #369

Merged
merged 15 commits into from
May 29, 2018
Merged

Conversation

nolanmar511
Copy link
Contributor

@nolanmar511 nolanmar511 commented May 1, 2018

Part of fixing #323.

Changes made to design:

  • Using key "base" and value "1" to mark profiles in the base profile, rather than key "pprof::base" (or "pprof::diff") and value "true", because we don't want to use long values (the are lots of samples, so adding lots of tags might increase memory usage too much).
  • Using -diff_base, rather than -diff as the flag name. It should be clearer.

@nolanmar511 nolanmar511 force-pushed the diff branch 3 times, most recently from daf6352 to a7b32db Compare May 1, 2018 16:14
@@ -140,7 +142,15 @@ func parseFlags(o *plugin.Options) (*source, []string, error) {
Comment: *flagAddComment,
}

for _, s := range *flagBase {
if len(*flagBase) > 0 && len(*flagDiff) > 0 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Compare strings for emptiness using x != "" so that it's clear it's not a slice.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Err, ignore, those are slices.

@@ -140,7 +142,15 @@ func parseFlags(o *plugin.Options) (*source, []string, error) {
Comment: *flagAddComment,
}

for _, s := range *flagBase {
if len(*flagBase) > 0 && len(*flagDiff) > 0 {
return nil, nil, fmt.Errorf("only -base or -diff flag can be specified")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The message could be clearer - it now reads as if no other flags can be passed other than the two mentioned.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adjusted the commented.


// Also remove pprof::diff=true string tag.
if values, ok := s.Label["pprof::diff"]; ok {
newVals := []string{}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var newVals []string

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code has been removed.

@@ -261,6 +261,21 @@ func (rpt *Report) newGraph(nodes graph.NodeSet) *graph.Graph {
}
s.NumLabel = numLabels
s.NumUnit = numUnits

// Also remove pprof::diff=true string tag.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to clarify why. Also this code is not super clear. Perhaps make a separate function, and look into how to simplify it. I would also look into whether code could be simpler if the pprof::diff tag was removed in the aggregate() code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would removing the tag in aggregation() prevent it from being used in later to separate out source and base profiles?

For now, I'm separating this into a different function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the SetTag function to clear pprof::diff tag.
And adding comment to explain why this tag shouldn't be set.

@@ -285,3 +285,122 @@ func TestLegendActiveFilters(t *testing.T) {
}
}
}

func TestComputTotal(t *testing.T) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -1211,10 +1226,10 @@ func NewDefault(prof *profile.Profile, options Options) *Report {
return New(prof, o)
}

// computeTotal computes the sum of all sample values. This will be
// used to compute percentages.
// computeTotal computes the sum of the absolute value of all sample values.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doc should be updated when such non-trivial logic is added in the function code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

total += v
if sample.HasTag("pprof::diff", "true") {
diffTotal += v
}
div += d
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming that it's intentional that accumulating the div is not under the tag condition, can you add a comment on why so?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed.

// profile.
func (p *Profile) AddTag(key, value string) {
for _, sample := range p.Sample {
if sample.HasTag(key, value) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling HasTag could be avoided and handled below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing AddTag to SetTag, so I think this no longer applies.

@@ -674,6 +674,36 @@ func numLabelsToString(numLabels map[string][]int64, numUnits map[string][]strin
return strings.Join(ls, " ")
}

// AddTag adds a tag with the specified key and value to all samples in the
// profile.
func (p *Profile) AddTag(key, value string) {
Copy link
Collaborator

@aalexand aalexand May 1, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd maybe do this as SetTag(), it would simplify the code and I don't think we really need to support multiple values per tag in this PR. Would simplify the tests as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

{
Location: []*profile.Location{testL[2], testL[1], testL[0]},
Value: []int64{-9000, 3},
Label: map[string][]string{"pprof::diff": {"true"}},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that "pprof::diff" and "true" are going to be stored as strings for each sample, I wonder if we should come up with shorter strings to save memory.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree -- maybe "1"?

@nolanmar511 nolanmar511 force-pushed the diff branch 8 times, most recently from 542d764 to c15f92c Compare May 2, 2018 19:42
@codecov-io
Copy link

codecov-io commented May 2, 2018

Codecov Report

Merging #369 into master will increase coverage by 0.18%.
The diff coverage is 88%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #369      +/-   ##
==========================================
+ Coverage   66.89%   67.08%   +0.18%     
==========================================
  Files          36       36              
  Lines        7495     7532      +37     
==========================================
+ Hits         5014     5053      +39     
+ Misses       2079     2078       -1     
+ Partials      402      401       -1
Impacted Files Coverage Δ
internal/driver/fetch.go 68.34% <100%> (+0.18%) ⬆️
profile/profile.go 70.92% <100%> (+1.14%) ⬆️
internal/driver/cli.go 79.6% <78.26%> (+2.06%) ⬆️
internal/report/report.go 31.33% <90.9%> (+0.97%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 4d67f66...9558b3c. Read the comment docs.

@nolanmar511
Copy link
Contributor Author

PTAL

doc/pprof.md Outdated
@@ -214,6 +214,10 @@ search for them in a directory pointed to by the environment variable
* **-weblist= _regex_:** Generates a source/assembly combined annotated listing for
functions matching *regex*, and starts a web browser to display it.

Samples in a profile may have tags. These tags have a name and a value; this
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This added paragraph sounds a bit out of tune in this "Annotated code" section. Why here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made an error when moving docs around. Fixed.

doc/pprof.md Outdated

The **-base** option can be used in the place of the **-diff_base** option, and
may be preferrable when comparing two cumulative profiles, for example two
contention profiles collected from the same program at different times. When
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be more specific that we are talking about contention reports from Go programs. Linking to Go's documentation would be good.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't find much in go documentation on contention reports.

This is the main thing I found (from here):

//	goroutine    - stack traces of all current goroutines
//	heap         - a sampling of all heap allocations
//	threadcreate - stack traces that led to the creation of new OS threads
//	block        - stack traces that led to blocking on synchronization primitives
//	mutex        - stack traces of holders of contended mutexes

I'm guessing that block profiles are what we would consider contention profiles?
I could look for blog posts?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe link to https://golang.org/doc/diagnostics.html#profiling? It's not exclusively about contention but has something.

doc/pprof.md Outdated
may be preferrable when comparing two cumulative profiles, for example two
contention profiles collected from the same program at different times. When
this flag is used and no negative values appear in the merged profile when
aggregated at the address-level, percentages reported will be relative to the
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd somehow leave out the detail about address level or move it closer to the end of the section. Telling the whole story feels overwhelming a bit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried to change this.
Possibly fixed.

doc/pprof.md Outdated

## Comparing profiles

pprof can subtract a profile from another, provided the profiles are of
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe start this section with a very high-level note that there are two ways to compare profiles, briefly characterize each of them, then talk about each in more detail.

doc/pprof.md Outdated
compatible types, in order to compare them. For that, use the
**-diff_base= _profile_** option, where *profile* is the filename or URL for the
profile to be subtracted. This may result in some report entries having negative
values. Percentages in the report are relative to the total of
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: double space is a typo?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed.

@@ -140,12 +142,30 @@ func parseFlags(o *plugin.Options) (*source, []string, error) {
Comment: *flagAddComment,
}

var base []string
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fragment of code of dealing with base and diffBase flags is a bit long now. Consider refactoring it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did some refactoring.

if sample.HasTag("pprof::base", "true") {
diffTotal += v
diffDiv += d
}
div += d
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I had this comment but it was not answered before: Assuming that it's intentional that accumulating the div is not under the tag condition, can you add a comment on why so?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

func (p *Profile) SetTag(key string, value []string) {
for _, sample := range p.Sample {
if sample.Label == nil {
sample.Label = make(map[string][]string)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sample.Label = map[string]string{} is a bit cheaper.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

}{
{
desc: "empty label does not have tag",
sample: &Sample{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you could avoid repeating the Sample and its Location and Value in every test case. You only care about Label field, you could reuse the rest.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

wantTags []map[string][]string
}{
{
desc: "some samples have tag already",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean to add more test cases? E.g. what if sample doesn't have the tags.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.
Also did some refactoring of this test.

@nolanmar511
Copy link
Contributor Author

PTAL, except for at docs.
(Will add another PTAL when docs are ready to be looked at again)

@nolanmar511
Copy link
Contributor Author

Also addressed (tried to address) comments on docs.
Those are ready for PTAL, too.

@nolanmar511
Copy link
Contributor Author

PTAL

},
}

// Allow more test cases to run in parallel.
runtime.GOMAXPROCS(len(testcases))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not understand why this is needed. You are not calling https://golang.org/pkg/testing/#T.Parallel here.

@aalexand
Copy link
Collaborator

aalexand commented May 9, 2018

@nolanmar511 Please LMK when you remove the runtime.GOMAXPROCS(len(testcases)) code and I'll look again.

@nolanmar511
Copy link
Contributor Author

Removed the runtime.GOMAXPROCS(len(testcases)) code.
PTAL

base := make([]*string, len(tc.bases))
for i, s := range tc.bases {
base[i] = &s
}

diffBase := make([]*string, len(tc.diffBases))
for i, s := range tc.diffBases {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: there is a similar loop above, maybe testFlags could be made having stringLists as slice of regular strings, not pointers and translate that to pointers when needed in its implementation. Very much optional suggestion - judge depending on how much noise in the test code this pointerization has.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

if err == nil {
t.Fatalf("got nil, want error %q", tc.wantErrorMsg)
}
gotErrMsg := err.Error()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merge assignment into the if statement on next line?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

if err != nil {
t.Fatal(err)
}

if want, got := len(tc.expectedSamples), len(p.Sample); want != got {
t.Fatalf("want %d samples got %d", want, got)
if want, got := len(tc.wantSamples), len(p.Sample); want != got {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super-nit: I'd reverse want, got into got, want on the assignment and condition as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -264,6 +264,10 @@ func (rpt *Report) newGraph(nodes graph.NodeSet) *graph.Graph {
s.NumUnit = numUnits
}

// Remove tag marking samples from the base profiles, so it does not appear
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tag -> label

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not done?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry -- just pushed.

// computeTotal computes the sum of all sample values. This will be
// used to compute percentages.
// computeTotal computes the sum of the absolute value of all sample values.
// If any samples have the tag "pprof::base" with value "true", then the total
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tag -> label

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@nolanmar511
Copy link
Contributor Author

PTAL

}

normalize := pprofVariables["normalize"].boolValue()
if normalize && len(source.Base) == 0 {
return nil, nil, fmt.Errorf("Must have base profile to normalize by")
return nil, nil, errors.New("Must have base profile to normalize by")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

return nil
}

// dropEmpty list takes a StringList flag, and outputs an array of non-empty
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not take a flag, it takes a slice of string pointers. It does not return an array, it returns a slice.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// profiles are specified.
func (source *source) addBaseProfiles(flagBase, flagDiffBase []*string) error {
base, diffBase := dropEmpty(flagBase), dropEmpty(flagDiffBase)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: remove empty line.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -355,7 +355,11 @@ func (f testFlags) StringVar(p *string, s, d, c string) {

func (f testFlags) StringList(s, d, c string) *[]*string {
if t, ok := f.stringLists[s]; ok {
return &t
tp := make([]*string, len(t))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change? It's not obvious, a comment would be useful.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was to address a previous comment for simplifying testing.
I changed the type of the stringLists field in the testFlags structure from map[string][]*string to map[string][]string to make it easier to construct flags for tests.

Added a comment.


for _, tc := range testcases {
t.Run(tc.desc, func(t *testing.T) {
gotTotal := computeTotal(tc.prof, tc.value, tc.meanDiv)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merge with next line.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// profile.
func (p *Profile) SetLabel(key string, value []string) {
for _, sample := range p.Sample {
if sample.Label == nil {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could avoid lookup on the "no key" case as

if sample.Label == nil {
  sample.Label = map[string][]string{key: value}
} else {
  sample.Label[key] = value
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// samples in the profile.
func (p *Profile) RemoveLabel(key string) {
for _, sample := range p.Sample {
if _, ok := sample.Label[key]; ok {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://blog.golang.org/go-maps-in-action: "The delete function doesn't return anything, and will do nothing if the specified key doesn't exist.", so I think you could drop the check?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


// HasLabel returns true if a sample has a label with indicated key and value.
func (s *Sample) HasLabel(key, value string) bool {
if values, ok := s.Label[key]; ok {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you could drop the "ok" check and just do

for _, v := range s.Label[key] {

as it will return nil slice if the key is missing which will immediately terminate the loop anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

for wantKey, wantValues := range wantLabels {
if gotValues, ok := sample.Label[wantKey]; ok {
if !reflect.DeepEqual(gotValues, wantValues) {
t.Fatalf("for key %s, got values %v, want values %v", wantKey, gotValues, wantValues)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why Fatalf?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Errorf works.
Done.

@nolanmar511 nolanmar511 force-pushed the diff branch 2 times, most recently from c0dc0bd to e1f7505 Compare May 24, 2018 19:49
@nolanmar511
Copy link
Contributor Author

PTAL

Copy link
Collaborator

@aalexand aalexand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just couple nits. Please also confirm you tested the new functionality manually too.

true,
[][]int64{},
[]WantSample{},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and elsewhere: use nil for empty slices.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

if err != nil {
t.Fatalf("%s: %v", tc.desc, err)
t.Fatal(err)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this perhaps should be t.Fatalf("got error %q, want no error", err).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@nolanmar511
Copy link
Contributor Author

PTAL

@nolanmar511
Copy link
Contributor Author

Did some manual testing. Looking at the graph view, the value for "Total samples" is equal to the total of the base profile.

Used -proto to output the profile, then used pprof again to inspect that profile, and saw same total reported with graph view.

@aalexand aalexand merged commit c283c2c into google:master May 29, 2018
KISSMonX pushed a commit to KISSMonX/pprof that referenced this pull request Jun 14, 2018
* 'master' of github.com:google/pprof: (32 commits)
  record diff base profile label key/value constant (google#384)
  apply additional command overrides based on report format (google#381)
  profile: fix legacy format Go heap profile parsing (google#382)
  add -diff flag for better profile comparision (google#369)
  Use -u flag in pprof installation command so that it updates if needed. (google#376)
  Add GetBase support for ASLR kernel mappings (google#371)
  Add show_from profile filter. (google#372)
  Update README.md due to 8dff45d (google#375)
  Remove stale docs, move useful ones. (google#374)
  internal/driver: skip tests requiring tcp on js (google#373)
  Add "trim path" option which can be used to relocate sources. (google#366)
  Move update_d3flamegraph.sh script from the root directory. (google#370)
  Skip unsymbolizable mapping during symbolz pass. (google#368)
  remove -positive_percentages flag (google#365)
  Render icicle graph in "Flame Graph" view (google#367)
  Add command-line editing support for interactive pprof (google#362)
  Improve profile filter unit tests, fix show filtering of mappings (google#354)
  Fake mappings should be merged into a single one during merging (google#357)
  Hack the code so that both existing Go versions and current Go master format it the same (google#358)
  moved filter tests into their own test file which matches the implementation file, filter.go. (google#356)
  ...
gmarin13 pushed a commit to gmarin13/pprof that referenced this pull request Dec 17, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants