ctdb-daemon: Use ctdb_parse_node_address() in ctdbd
[samba4-gss.git] / third_party / waf / waflib / extras / stracedeps.py
blob37d82cbb724b14f4068415f2d49d3e9f60df20a9
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # Thomas Nagy, 2015 (ita)
5 """
6 Execute tasks through strace to obtain dependencies after the process is run. This
7 scheme is similar to that of the Fabricate script.
9 To use::
11 def configure(conf):
12 conf.load('strace')
14 WARNING:
15 * This will not work when advanced scanners are needed (qt4/qt5)
16 * The overhead of running 'strace' is significant (56s -> 1m29s)
17 * It will not work on Windows :-)
18 """
20 import os, re, threading
21 from waflib import Task, Logs, Utils
23 #TRACECALLS = 'trace=access,chdir,clone,creat,execve,exit_group,fork,lstat,lstat64,mkdir,open,rename,stat,stat64,symlink,vfork'
24 TRACECALLS = 'trace=process,file'
26 BANNED = ('/tmp', '/proc', '/sys', '/dev')
28 s_process = r'(?:clone|fork|vfork)\(.*?(?P<npid>\d+)'
29 s_file = r'(?P<call>\w+)\("(?P<path>([^"\\]|\\.)*)"(.*)'
30 re_lines = re.compile(r'^(?P<pid>\d+)\s+(?:(?:%s)|(?:%s))\r*$' % (s_file, s_process), re.IGNORECASE | re.MULTILINE)
31 strace_lock = threading.Lock()
33 def configure(conf):
34 conf.find_program('strace')
36 def task_method(func):
37 # Decorator function to bind/replace methods on the base Task class
39 # The methods Task.exec_command and Task.sig_implicit_deps already exists and are rarely overridden
40 # we thus expect that we are the only ones doing this
41 try:
42 setattr(Task.Task, 'nostrace_%s' % func.__name__, getattr(Task.Task, func.__name__))
43 except AttributeError:
44 pass
45 setattr(Task.Task, func.__name__, func)
46 return func
48 @task_method
49 def get_strace_file(self):
50 try:
51 return self.strace_file
52 except AttributeError:
53 pass
55 if self.outputs:
56 ret = self.outputs[0].abspath() + '.strace'
57 else:
58 ret = '%s%s%d%s' % (self.generator.bld.bldnode.abspath(), os.sep, id(self), '.strace')
59 self.strace_file = ret
60 return ret
62 @task_method
63 def get_strace_args(self):
64 return (self.env.STRACE or ['strace']) + ['-e', TRACECALLS, '-f', '-o', self.get_strace_file()]
66 @task_method
67 def exec_command(self, cmd, **kw):
68 bld = self.generator.bld
69 if not 'cwd' in kw:
70 kw['cwd'] = self.get_cwd()
72 args = self.get_strace_args()
73 fname = self.get_strace_file()
74 if isinstance(cmd, list):
75 cmd = args + cmd
76 else:
77 cmd = '%s %s' % (' '.join(args), cmd)
79 try:
80 ret = bld.exec_command(cmd, **kw)
81 finally:
82 if not ret:
83 self.parse_strace_deps(fname, kw['cwd'])
84 return ret
86 @task_method
87 def sig_implicit_deps(self):
88 # bypass the scanner functions
89 return
91 @task_method
92 def parse_strace_deps(self, path, cwd):
93 # uncomment the following line to disable the dependencies and force a file scan
94 # return
95 try:
96 cnt = Utils.readf(path)
97 finally:
98 try:
99 os.remove(path)
100 except OSError:
101 pass
103 if not isinstance(cwd, str):
104 cwd = cwd.abspath()
106 nodes = []
107 bld = self.generator.bld
108 try:
109 cache = bld.strace_cache
110 except AttributeError:
111 cache = bld.strace_cache = {}
113 # chdir and relative paths
114 pid_to_cwd = {}
116 global BANNED
117 done = set()
118 for m in re.finditer(re_lines, cnt):
119 # scraping the output of strace
120 pid = m.group('pid')
121 if m.group('npid'):
122 npid = m.group('npid')
123 pid_to_cwd[npid] = pid_to_cwd.get(pid, cwd)
124 continue
126 p = m.group('path').replace('\\"', '"')
128 if p == '.' or m.group().find('= -1 ENOENT') > -1:
129 # just to speed it up a bit
130 continue
132 if not os.path.isabs(p):
133 p = os.path.join(pid_to_cwd.get(pid, cwd), p)
135 call = m.group('call')
136 if call == 'chdir':
137 pid_to_cwd[pid] = p
138 continue
140 if p in done:
141 continue
142 done.add(p)
144 for x in BANNED:
145 if p.startswith(x):
146 break
147 else:
148 if p.endswith('/') or os.path.isdir(p):
149 continue
151 try:
152 node = cache[p]
153 except KeyError:
154 strace_lock.acquire()
155 try:
156 cache[p] = node = bld.root.find_node(p)
157 if not node:
158 continue
159 finally:
160 strace_lock.release()
161 nodes.append(node)
163 # record the dependencies then force the task signature recalculation for next time
164 if Logs.verbose:
165 Logs.debug('deps: real scanner for %r returned %r', self, nodes)
166 bld = self.generator.bld
167 bld.node_deps[self.uid()] = nodes
168 bld.raw_deps[self.uid()] = []
169 try:
170 del self.cache_sig
171 except AttributeError:
172 pass
173 self.signature()