diff options
author | George Burgess IV <gbiv@google.com> | 2020-10-16 15:54:21 -0700 |
---|---|---|
committer | George Burgess <gbiv@chromium.org> | 2020-10-23 19:03:34 +0000 |
commit | 84b5c8fad9e67e74fd14d26fc45a03329f3d1b56 (patch) | |
tree | 8faf49760a232ea83b0364fb4a6e9b4f8a21105b /compiler_wrapper | |
parent | 49187a5efe3ee2e23d99484a1d25500f116dd4f7 (diff) | |
download | toolchain-utils-84b5c8fad9e67e74fd14d26fc45a03329f3d1b56.tar.gz |
compiler_wrapper: report parent processes' info in warnings tarballs
The bug I'm chasing down right now depends on environment variables, and
could be due to a `make` invocation in one of N places. Having this
context available to me is useful.
It seriously pollutes the JSON we dump, but use of `jq` that I've put in
the doc about this makes that a non-issue.
BUG=None
TEST=Ran on stress-ng. parent process info was dumped.
Change-Id: Ib4d3b6c9887b4cf2a38472ef299c81af27e9a617
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/2481829
Reviewed-by: Manoj Gupta <manojgupta@chromium.org>
Tested-by: George Burgess <gbiv@chromium.org>
Diffstat (limited to 'compiler_wrapper')
-rw-r--r-- | compiler_wrapper/disable_werror_flag.go | 100 | ||||
-rw-r--r-- | compiler_wrapper/disable_werror_flag_test.go | 65 |
2 files changed, 157 insertions, 8 deletions
diff --git a/compiler_wrapper/disable_werror_flag.go b/compiler_wrapper/disable_werror_flag.go index 1180127c..8f20b6f3 100644 --- a/compiler_wrapper/disable_werror_flag.go +++ b/compiler_wrapper/disable_werror_flag.go @@ -7,9 +7,12 @@ package main import ( "bytes" "encoding/json" + "fmt" "io" "io/ioutil" "os" + "path" + "strconv" "strings" "syscall" ) @@ -119,10 +122,14 @@ func doubleBuildWithWNoError(env env, cfg *config, originalCmd *command) (exitCo } outputToLog := strings.Join(lines, "\n") + // Ignore the error here; we can't do anything about it. The result is always valid (though + // perhaps incomplete) even if this returns an error. + parentProcesses, _ := collectAllParentProcesses() jsonData := warningsJSONData{ - Cwd: env.getwd(), - Command: append([]string{originalCmd.Path}, originalCmd.Args...), - Stdout: outputToLog, + Cwd: env.getwd(), + Command: append([]string{originalCmd.Path}, originalCmd.Args...), + Stdout: outputToLog, + ParentProcesses: parentProcesses, } // Write warning report to stdout for Android. On Android, @@ -186,10 +193,87 @@ func doubleBuildWithWNoError(env env, cfg *config, originalCmd *command) (exitCo return retryExitCode, nil } -// Struct used to write JSON. Fileds have to be uppercase for the json -// encoder to read them. +func parseParentPidFromPidStat(pidStatContents string) (parentPid int, ok bool) { + // The parent's pid is the fourth field of /proc/[pid]/stat. Sadly, the second field can + // have spaces in it. It ends at the last ')' in the contents of /proc/[pid]/stat. + lastParen := strings.LastIndex(pidStatContents, ")") + if lastParen == -1 { + return 0, false + } + + thirdFieldAndBeyond := strings.TrimSpace(pidStatContents[lastParen+1:]) + fields := strings.Fields(thirdFieldAndBeyond) + if len(fields) < 2 { + return 0, false + } + + fourthField := fields[1] + parentPid, err := strconv.Atoi(fourthField) + if err != nil { + return 0, false + } + return parentPid, true +} + +func collectProcessData(pid int) (args, env []string, parentPid int, err error) { + procDir := fmt.Sprintf("/proc/%d", pid) + + readFile := func(fileName string) (string, error) { + s, err := ioutil.ReadFile(path.Join(procDir, fileName)) + if err != nil { + return "", fmt.Errorf("reading %s: %v", fileName, err) + } + return string(s), nil + } + + statStr, err := readFile("stat") + if err != nil { + return nil, nil, 0, err + } + + parentPid, ok := parseParentPidFromPidStat(statStr) + if !ok { + return nil, nil, 0, fmt.Errorf("no parseable parent PID found in %q", statStr) + } + + argsStr, err := readFile("cmdline") + if err != nil { + return nil, nil, 0, err + } + args = strings.Split(argsStr, "\x00") + + envStr, err := readFile("environ") + if err != nil { + return nil, nil, 0, err + } + env = strings.Split(envStr, "\x00") + return args, env, parentPid, nil +} + +// The returned []processData is valid even if this returns an error. The error is just the first we +// encountered when trying to collect parent process data. +func collectAllParentProcesses() ([]processData, error) { + results := []processData{} + for parent := os.Getppid(); parent != 1; { + args, env, p, err := collectProcessData(parent) + if err != nil { + return results, fmt.Errorf("inspecting parent %d: %v", parent, err) + } + results = append(results, processData{Args: args, Env: env}) + parent = p + } + return results, nil +} + +type processData struct { + Args []string `json:"invocation"` + Env []string `json:"env"` +} + +// Struct used to write JSON. Fields have to be uppercase for the json encoder to read them. type warningsJSONData struct { - Cwd string `json:"cwd"` - Command []string `json:"command"` - Stdout string `json:"stdout"` + Cwd string `json:"cwd"` + Command []string `json:"command"` + Stdout string `json:"stdout"` + ParentProcesses []processData `json:"parent_process_data"` } diff --git a/compiler_wrapper/disable_werror_flag_test.go b/compiler_wrapper/disable_werror_flag_test.go index ecc1090b..0c43dbe0 100644 --- a/compiler_wrapper/disable_werror_flag_test.go +++ b/compiler_wrapper/disable_werror_flag_test.go @@ -500,3 +500,68 @@ func TestClangTidyDoubleBuildClangError(t *testing.T) { } }) } + +func TestProcPidStatParsingWorksAsIntended(t *testing.T) { + t.Parallel() + + type expected struct { + parent int + ok bool + } + + testCases := []struct { + input string + expected expected + }{ + { + input: "2556041 (cat) R 2519408 2556041 2519408 34818 2556041 4194304", + expected: expected{ + parent: 2519408, + ok: true, + }, + }, + { + input: "2556041 (c a t) R 2519408 2556041 2519408 34818 2556041 4194304", + expected: expected{ + parent: 2519408, + ok: true, + }, + }, + { + input: "", + expected: expected{ + ok: false, + }, + }, + { + input: "foo (bar)", + expected: expected{ + ok: false, + }, + }, + { + input: "foo (bar) baz", + expected: expected{ + ok: false, + }, + }, + { + input: "foo (bar) baz 1qux2", + expected: expected{ + ok: false, + }, + }, + } + + for _, tc := range testCases { + parent, ok := parseParentPidFromPidStat(tc.input) + if tc.expected.ok != ok { + t.Errorf("Got ok=%v when parsing %q; expected %v", ok, tc.input, tc.expected.ok) + continue + } + + if tc.expected.parent != parent { + t.Errorf("Got parent=%v when parsing %q; expected %v", parent, tc.input, tc.expected.parent) + } + } +} |