aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge Burgess IV <gbiv@google.com>2020-10-16 15:54:21 -0700
committerGeorge Burgess <gbiv@chromium.org>2020-10-23 19:03:34 +0000
commit84b5c8fad9e67e74fd14d26fc45a03329f3d1b56 (patch)
tree8faf49760a232ea83b0364fb4a6e9b4f8a21105b
parent49187a5efe3ee2e23d99484a1d25500f116dd4f7 (diff)
downloadtoolchain-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>
-rw-r--r--compiler_wrapper/disable_werror_flag.go100
-rw-r--r--compiler_wrapper/disable_werror_flag_test.go65
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)
+ }
+ }
+}