Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # Copyright 2013 The Chromium Authors. All rights reserved. | 2 # Copyright 2013 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 # Use: ../mergetraces.py `ls cyglog.* -Sr` > merged_cyglog | 6 # Use: ../mergetraces.py `ls cyglog.* -Sr` > merged_cyglog |
| 7 | 7 |
| 8 """"Merge multiple logs files from different processes into a single log. | 8 """"Merge multiple logs files from different processes into a single log. |
| 9 | 9 |
| 10 Given two log files of execution traces, merge the traces into a single trace. | 10 Given two log files of execution traces, merge the traces into a single trace. |
| 11 Merging will use timestamps (i.e. the first two columns of logged calls) to | 11 Merging will use timestamps (i.e. the first two columns of logged calls) to |
| 12 create a single log that is an ordered trace of calls by both processes. | 12 create a single log that is an ordered trace of calls by both processes. |
| 13 """ | 13 """ |
| 14 | 14 |
| 15 import optparse | 15 import optparse |
| 16 import string | 16 import string |
| 17 import sets | |
| 17 import sys | 18 import sys |
| 18 | 19 |
| 19 def ParseLogLines(lines): | 20 def ParseLogLines(lines): |
| 20 """Parse log file lines. | 21 """Parse log file lines. |
| 21 | 22 |
| 22 Args: | 23 Args: |
| 23 lines: lines from log file produced by profiled run | 24 lines: lines from log file produced by profiled run |
| 24 | 25 |
| 25 Below is an example of a small log file: | 26 Below is an example of a small log file: |
| 26 5086e000-52e92000 r-xp 00000000 b3:02 51276 libchromeview.so | 27 5086e000-52e92000 r-xp 00000000 b3:02 51276 libchromeview.so |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 136 tracemap[call] = trace_entry | 137 tracemap[call] = trace_entry |
| 137 | 138 |
| 138 def GroupByProcessAndThreadId(input_trace): | 139 def GroupByProcessAndThreadId(input_trace): |
| 139 """Returns an array of traces grouped by pid and tid. | 140 """Returns an array of traces grouped by pid and tid. |
| 140 | 141 |
| 141 This is used to make the order of functions not depend on thread scheduling | 142 This is used to make the order of functions not depend on thread scheduling |
| 142 which can be greatly impacted when profiling is done with cygprofile. As a | 143 which can be greatly impacted when profiling is done with cygprofile. As a |
| 143 result each thread has its own contiguous segment of code (ordered by | 144 result each thread has its own contiguous segment of code (ordered by |
| 144 timestamp) and processes also have their code isolated (i.e. not interleaved). | 145 timestamp) and processes also have their code isolated (i.e. not interleaved). |
| 145 """ | 146 """ |
| 146 def MakeTimestamp(usec, sec): | 147 def MakeTimestamp(sec, usec): |
| 147 return usec * 1000000 + sec | 148 return sec * 1000000 + usec |
| 148 | 149 |
| 149 def PidAndTidFromString(pid_and_tid): | 150 def PidAndTidFromString(pid_and_tid): |
| 150 strings = pid_and_tid.split(':') | 151 strings = pid_and_tid.split(':') |
| 151 return (int(strings[0]), int(strings[1])) | 152 return (int(strings[0]), int(strings[1])) |
| 152 | 153 |
| 154 tid_to_pids_map = {} | |
| 153 pid_first_seen = {} | 155 pid_first_seen = {} |
| 154 tid_first_seen = {} | 156 tid_first_seen = {} |
| 157 | |
| 155 for (sec, usec, pid_and_tid, _) in input_trace: | 158 for (sec, usec, pid_and_tid, _) in input_trace: |
| 156 (pid, tid) = PidAndTidFromString(pid_and_tid) | 159 (pid, tid) = PidAndTidFromString(pid_and_tid) |
| 160 | |
| 161 # Make sure that thread IDs are unique since this a property we rely on. | |
|
pasko
2014/06/06 09:41:44
s/this/this is/
Philippe
2014/06/06 09:50:23
Done.
Philippe
2014/06/06 09:50:23
Done.
| |
| 162 pids_for_tid = tid_to_pids_map.setdefault(tid, sets.Set()) | |
| 163 pids_for_tid.add(pid) | |
| 164 if len(pids_for_tid) > 1: | |
| 165 raise Exception('Thread IDs should be unique.') | |
|
pasko
2014/06/06 09:41:44
in this context we actually have the tid and two p
Philippe
2014/06/06 09:50:23
Yeah :) I haven't slept much last night :)
| |
| 166 | |
| 157 if not pid in pid_first_seen: | 167 if not pid in pid_first_seen: |
| 158 pid_first_seen[pid] = MakeTimestamp(usec, sec) | 168 pid_first_seen[pid] = MakeTimestamp(sec, usec) |
| 159 if not tid in tid_first_seen: | 169 if not tid in tid_first_seen: |
| 160 tid_first_seen[tid] = MakeTimestamp(usec, sec) | 170 tid_first_seen[tid] = MakeTimestamp(sec, usec) |
| 161 | 171 |
| 162 def CompareEvents(event1, event2): | 172 def CompareEvents(event1, event2): |
| 163 (sec1, usec1, pid_and_tid, _) = event1 | 173 (sec1, usec1, pid_and_tid, _) = event1 |
| 164 (pid1, tid1) = PidAndTidFromString(pid_and_tid) | 174 (pid1, tid1) = PidAndTidFromString(pid_and_tid) |
| 165 (sec2, usec2, pid_and_tid, _) = event2 | 175 (sec2, usec2, pid_and_tid, _) = event2 |
| 166 (pid2, tid2) = PidAndTidFromString(pid_and_tid) | 176 (pid2, tid2) = PidAndTidFromString(pid_and_tid) |
| 167 | 177 |
| 168 pid_cmp = cmp(pid_first_seen[pid1], pid_first_seen[pid2]) | 178 pid_cmp = cmp(pid_first_seen[pid1], pid_first_seen[pid2]) |
| 169 if pid_cmp != 0: | 179 if pid_cmp != 0: |
| 170 return pid_cmp | 180 return pid_cmp |
| 171 tid_cmp = cmp(tid_first_seen[tid1], tid_first_seen[tid2]) | 181 tid_cmp = cmp(tid_first_seen[tid1], tid_first_seen[tid2]) |
| 172 if tid_cmp != 0: | 182 if tid_cmp != 0: |
| 173 return tid_cmp | 183 return tid_cmp |
| 174 return cmp(MakeTimestamp(usec1, sec1), MakeTimestamp(usec2, sec2)) | 184 return cmp(MakeTimestamp(sec1, usec1), MakeTimestamp(sec2, usec2)) |
| 175 | 185 |
| 176 return sorted(input_trace, cmp=CompareEvents) | 186 return sorted(input_trace, cmp=CompareEvents) |
| 177 | 187 |
| 178 def main(): | 188 def main(): |
| 179 """Merge two traces for code in specified library and write to stdout. | 189 """Merge two traces for code in specified library and write to stdout. |
| 180 | 190 |
| 181 Merges the two traces and coverts the virtual addresses to the offsets in the | 191 Merges the two traces and coverts the virtual addresses to the offsets in the |
| 182 library. First line of merged trace has dummy virtual address of 0-ffffffff | 192 library. First line of merged trace has dummy virtual address of 0-ffffffff |
| 183 so that symbolizing the addresses uses the addresses in the log, since the | 193 so that symbolizing the addresses uses the addresses in the log, since the |
| 184 addresses have already been converted to static offsets. | 194 addresses have already been converted to static offsets. |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 220 grouped_trace = GroupByProcessAndThreadId(merged_trace) | 230 grouped_trace = GroupByProcessAndThreadId(merged_trace) |
| 221 | 231 |
| 222 print "0-ffffffff r-xp 00000000 xx:00 00000 ./" | 232 print "0-ffffffff r-xp 00000000 xx:00 00000 ./" |
| 223 print "secs\tusecs\tpid:threadid\tfunc" | 233 print "secs\tusecs\tpid:threadid\tfunc" |
| 224 for call in grouped_trace: | 234 for call in grouped_trace: |
| 225 print (str(call[0]) + "\t" + str(call[1]) + "\t" + call[2] + "\t" + | 235 print (str(call[0]) + "\t" + str(call[1]) + "\t" + call[2] + "\t" + |
| 226 hex(call[3])) | 236 hex(call[3])) |
| 227 | 237 |
| 228 if __name__ == '__main__': | 238 if __name__ == '__main__': |
| 229 main() | 239 main() |
| OLD | NEW |