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

Strace 4.24 #4

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 31 additions & 12 deletions depstrace.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@
_STRACE_LOG = 'strace_log.txt'
_STRACE_FIFO = '/tmp/_strace_log_fifo' # TODO: use tempfile

_FILEOPS=r'open|(sym)?link|rename|chdir|creat' # TODO: handle |openat?
_FILEOPS=r'open|(sym)?link|rename|chdir|creat|openat' # TODO: handle |openat?
_PROCOPS=r'clone|execve|v?fork'
_UNUSED=r'l?chown(32)?|[gs]etxattr|fchmodat|rmdir|mkdir|unlinkat|utimensat|getcwd|chmod|statfs(64)?|l?stat(64)?|access|readlink|unlink|exit_group|waitpid|wait4|arch_prctl|utime'
_UNUSED=r'l?chown(32)?|[gs]etxattr|fchmodat|rmdir|mkdir|unlinkat|utimensat|getcwd|chmod|statfs(64)?|l?stat(64)?|newfstatat|access|faccessat|readlink|unlink|exit_group|waitpid|wait4|arch_prctl|utime|exit'

_ARG = (r'\{[^}]+\}|' + # {st_mode=S_IFREG|0755, st_size=97736, ...}
r'"[^"]+"|' + # "tst.o"
r'\[[^]]+\]|' + # [{WIFEXITED(s) && WEXITSTATUS(s) == 0}]
# 0x7ffc39d83858 /* 104 vars */
r'0x[0-9a-fA-F]+\s+/\*\s+[0-9]+\s+vars\s+\*/\s*|' +
r'\S+') # O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, et. al.
_OPS = '%s|%s|%s' % (_FILEOPS, _PROCOPS, _UNUSED)

Expand Down Expand Up @@ -107,13 +109,14 @@ class DepsTracer(object):
# Regular expressions for parsing syscall in strace log
# TODO: this is VERY slow. We can easily improve this if anyone cares...
_file_re = re.compile(r'(?P<pid>\d+)\s+' +
r'(?P<op>%s)\(' % _OPS +
r'(?P<arg1>%s)?(, (?P<arg2>%s))?(, (%s))*' % (_ARG,_ARG,_ARG) +
r'\) = (?P<ret>-?\d+|\?)')
r'(?P<op>%s)\(\s*' % _OPS +
r'(?P<arg1>%s)?(,\s*(?P<arg2>%s))?(,\s*(?P<arg3>%s))?(,\s*(%s))*' % (_ARG,_ARG,_ARG,_ARG) +
r'\s*\) = (?P<ret>-?\d+|\?)')

# Regular expressions for joining interrupted lines in strace log
_unfinished_re = re.compile(r'(?P<body>(?P<pid>\d+).*)\s+<unfinished \.\.\.>$')
_resumed_re = re.compile(r'(?P<pid>\d+)\s+<\.\.\. \S+ resumed> (?P<body>.*)')
_resumed_re = re.compile(r'(?P<pid>\d+)\s+<\.\.\. \S+ resumed>(?P<body>.*)')
_exitted_re = re.compile(r'(?P<pid>\d+)\s+\+\+\+ exited with \d+ \+\+\+')

def __init__(self, build_dir=None, strict=False):
self._test_strace_version()
Expand Down Expand Up @@ -191,7 +194,7 @@ def trace(self, cmd):
'-f', # Follow child processes
'-a1', # Only one space before return values
'-s0', # Print non-filename strings really short to keep parser simpler
'-etrace=file,process', # Trace syscals related to file and process operations (*)
'-etrace=%file,%process', # Trace syscals related to file and process operations (*)
'-esignal=none'] + cmd
V1("Running: %r" % command)

Expand All @@ -210,7 +213,7 @@ def parse_trace(self, strace_out):

# Look for 'ninja' process invocation
ninja_pid = None
for pid, op, ret, arg1, _ in log_iterator:
for pid, op, ret, arg1, _, _ in log_iterator:
if op == 'execve' and ret == '0':
path = os.path.normpath(arg1)
if path.endswith(_NINJA_PROG_NAME):
Expand All @@ -224,7 +227,7 @@ def parse_trace(self, strace_out):
# Track processes spawn under 'ninja' and record their inputs/outputs,
# grouped by 'rule'. 'Rule' is considered to be process tree
# parented directly under 'ninja'.
for pid, op, ret, arg1, arg2 in log_iterator:
for pid, op, ret, arg1, arg2, arg3 in log_iterator:
# Ignore failed syscalls
if ret == '-1':
continue
Expand Down Expand Up @@ -255,6 +258,17 @@ def parse_trace(self, strace_out):
self.add_dep(pid, path)
else:
self.add_output(pid, path)
elif op == 'openat':
# should test arg1 == AT_FDCWD if arg2 is not absolute
path = self.norm_path(cwd, arg2)
mode = arg3
if 'O_DIRECTORY' in mode:
# Filter out 'opendir'-s.TBD: does this test worth the cycles?
continue
if 'O_RDONLY' in mode:
self.add_dep(pid, path)
else:
self.add_output(pid, path)
elif op == 'execve':
path = self.norm_path(cwd, arg1)
self.add_dep(pid, path)
Expand Down Expand Up @@ -302,6 +316,10 @@ def _strace_log_iter(self, strace_log):
continue
line = interrupted_syscalls[pid] + body
del interrupted_syscalls[pid]
match = self._exitted_re.match(line)
if match:
pid = match.group('pid')
continue

# Parse syscall line
fop = self._file_re.match(line)
Expand All @@ -310,11 +328,12 @@ def _strace_log_iter(self, strace_log):
continue

pid, op, ret = fop.group('pid'), fop.group('op'), fop.group('ret')
arg1, arg2 = fop.group('arg1'), fop.group('arg2')
arg1, arg2, arg3 = fop.group('arg1'), fop.group('arg2'), fop.group('arg3')
arg1 = arg1.strip('"') if arg1 else arg1
arg2 = arg2.strip('"') if arg2 else arg2
V2("pid=%s, op='%s', arg1=%s, arg2=%s, ret=%s" % (pid, op, arg1, arg2, ret))
yield (pid, op, ret, arg1, arg2)
arg3 = arg3.strip('"') if arg3 else arg3
V2("pid=%s, op='%s', arg1=%s, arg2=%s, arg3=%s, ret=%s" % (pid, op, arg1, arg2, arg3, ret))
yield (pid, op, ret, arg1, arg2, arg3)
if interrupted_syscalls:
warn("excessive interrupted syscall(s) at the end of trace:")
for k, v in interrupted_syscalls.iteritems():
Expand Down