25
25
from __future__ import print_function
26
26
27
27
import argparse
28
+ import datetime
28
29
import os
29
30
import subprocess
31
+ import sys
30
32
31
33
try :
32
34
import lldb
@@ -36,10 +38,17 @@ except ImportError:
36
38
if swift_exec is not None :
37
39
site_packages = os .path .join (os .path .dirname (swift_exec ),
38
40
'../lib/python2.7/site-packages/' )
39
- import sys
40
41
sys .path .append (site_packages )
41
42
import lldb
42
43
44
+ lldb_target = None
45
+ known_memmap = {}
46
+
47
+
48
+ def print_with_flush (buff ):
49
+ print (buff )
50
+ sys .stdout .flush ()
51
+
43
52
44
53
def process_ldd (lddoutput ):
45
54
dyn_libs = {}
@@ -53,28 +62,18 @@ def process_ldd(lddoutput):
53
62
return dyn_libs
54
63
55
64
56
- def create_lldb_target (binary , memmap ):
57
- lldb_debugger = lldb .SBDebugger .Create ()
58
- lldb_target = lldb_debugger .CreateTarget (binary )
59
- module = lldb_target .GetModuleAtIndex (0 )
65
+ def setup_lldb_target (binary , memmap ):
66
+ global lldb_target
67
+ if not lldb_target :
68
+ lldb_debugger = lldb .SBDebugger .Create ()
69
+ lldb_target = lldb_debugger .CreateTarget (binary )
70
+ module = lldb_target .GetModuleAtIndex (0 )
60
71
for dynlib_path in memmap :
61
72
module = lldb_target .AddModule (
62
73
dynlib_path , lldb .LLDB_ARCH_DEFAULT , None , None )
63
74
text_section = module .FindSection (".text" )
64
75
slide = text_section .GetFileAddress () - text_section .GetFileOffset ()
65
76
lldb_target .SetModuleLoadAddress (module , memmap [dynlib_path ] - slide )
66
- return lldb_target
67
-
68
-
69
- def add_lldb_target_modules (lldb_target , memmap ):
70
- for dynlib_path in memmap :
71
- module = lldb_target .AddModule (
72
- dynlib_path , lldb .LLDB_ARCH_DEFAULT , None , None )
73
- lldb_target .SetModuleLoadAddress (module , memmap [dynlib_path ])
74
-
75
-
76
- lldb_target = None
77
- known_memmap = {}
78
77
79
78
80
79
def check_base_address (dynlib_path , dynlib_baseaddr , memmap ):
@@ -116,11 +115,12 @@ def symbolicate_one(frame_addr, frame_idx, dynlib_fname):
116
115
frame_fragment , symbol_fragment , line_fragment )
117
116
118
117
119
- def process_stack (binary , dyn_libs , stack ):
118
+ def get_processed_stack (binary , dyn_libs , stack ):
120
119
global lldb_target
121
120
global known_memmap
121
+ processed_stack = []
122
122
if len (stack ) == 0 :
123
- return
123
+ return processed_stack
124
124
memmap = {}
125
125
full_stack = []
126
126
for line in stack :
@@ -150,20 +150,68 @@ def process_stack(binary, dyn_libs, stack):
150
150
full_stack .append (
151
151
{"line" : line , "framePC" : framePC , "dynlib_fname" : dynlib_fname })
152
152
153
- if lldb_target is None :
154
- lldb_target = create_lldb_target (binary , memmap )
155
- else :
156
- add_lldb_target_modules (lldb_target , memmap )
157
- frame_idx = 0
158
- for frame in full_stack :
153
+ setup_lldb_target (binary , memmap )
154
+
155
+ for frame_idx , frame in enumerate (full_stack ):
159
156
frame_addr = frame ["framePC" ]
160
157
dynlib_fname = frame ["dynlib_fname" ]
161
158
try :
162
159
sym_line = symbolicate_one (frame_addr , frame_idx , dynlib_fname )
163
- print (sym_line )
160
+ processed_stack . append (sym_line )
164
161
except Exception :
165
- print (frame ["line" ].rstrip ())
166
- frame_idx = frame_idx + 1
162
+ processed_stack .append (frame ["line" ].rstrip ())
163
+
164
+ return processed_stack
165
+
166
+
167
+ def is_fatal_error (line ):
168
+ return line .startswith ("Fatal error:" )
169
+
170
+
171
+ def is_stack_trace_header (line ):
172
+ return line .startswith ("Current stack trace:" )
173
+
174
+
175
+ def should_print_previous_line (current_line , previous_line ):
176
+ return is_fatal_error (previous_line ) and \
177
+ not is_stack_trace_header (current_line )
178
+
179
+
180
+ def should_print_current_line (current_line , previous_line ):
181
+ return (not is_fatal_error (current_line ) and
182
+ not is_stack_trace_header (current_line )) or \
183
+ (is_stack_trace_header (current_line ) and
184
+ not is_fatal_error (previous_line ))
185
+
186
+
187
+ def fatal_error_with_stack_trace_found (current_line , previous_line ):
188
+ return is_stack_trace_header (current_line ) and \
189
+ is_fatal_error (previous_line )
190
+
191
+
192
+ def print_stack (fatal_error_header ,
193
+ fatal_error_stack_trace_header ,
194
+ fatal_log_format ,
195
+ processed_stack ):
196
+ if not fatal_error_header :
197
+ for line in processed_stack :
198
+ print_with_flush (line )
199
+ else :
200
+ # fatal error with a stack trace
201
+ stack_str = fatal_error_header + fatal_error_stack_trace_header + \
202
+ '\n ' .join (processed_stack )
203
+ formatted_output = fatal_log_format
204
+
205
+ if "%t" in formatted_output :
206
+ current_time = datetime .datetime .now ()
207
+ time_in_iso_format = \
208
+ current_time .strftime ('%Y-%m-%dT%H:%M:%S,%f%z' )
209
+ formatted_output = \
210
+ formatted_output .replace ("%t" , time_in_iso_format )
211
+ if "%m" in formatted_output :
212
+ formatted_output = formatted_output .replace ("%m" , stack_str )
213
+
214
+ print_with_flush (formatted_output )
167
215
168
216
169
217
def main ():
@@ -175,34 +223,69 @@ def main():
175
223
parser .add_argument (
176
224
"log" , nargs = '?' , type = argparse .FileType ("rU" ), default = "-" ,
177
225
help = "Log file for symbolication. Defaults to stdin." )
226
+ parser .add_argument (
227
+ "--fatal-log-format" , default = "%m" ,
228
+ help = "Format for logging fatal errors. Variable %%t will be "
229
+ "replaced with current time in ISO 8601 format, variable "
230
+ "%%m will be replaced with the error message with a full "
231
+ "stack trace." )
178
232
args = parser .parse_args ()
179
233
180
234
binary = args .binary
235
+ fatal_log_format = args .fatal_log_format
181
236
182
237
lddoutput = subprocess .check_output (
183
238
['ldd' , binary ], stderr = subprocess .STDOUT )
184
239
dyn_libs = process_ldd (lddoutput )
185
240
186
241
instack = False
242
+ previous_line = ""
187
243
stackidx = 0
188
244
stack = []
245
+ fatal_error_header = ""
246
+ fatal_error_stack_trace_header = ""
189
247
190
248
while True :
191
- line = args .log .readline ()
192
- if not line :
249
+ current_line = args .log .readline ()
250
+ if not current_line :
193
251
break
194
- if instack and line .startswith (str (stackidx )):
195
- stack .append (line )
252
+ if instack and current_line .startswith (str (stackidx )):
253
+ stack .append (current_line )
196
254
stackidx = stackidx + 1
197
255
else :
256
+ processed_stack = get_processed_stack (binary , dyn_libs , stack )
257
+ print_stack (fatal_error_header ,
258
+ fatal_error_stack_trace_header ,
259
+ fatal_log_format ,
260
+ processed_stack )
261
+
198
262
instack = False
199
263
stackidx = 0
200
- process_stack (binary , dyn_libs , stack )
201
264
stack = []
202
- print (line .rstrip ())
203
- if line .startswith ("Current stack trace:" ):
204
- instack = True
205
- process_stack (binary , dyn_libs , stack )
265
+ fatal_error_header = ""
266
+ fatal_error_stack_trace_header = ""
267
+
268
+ if is_stack_trace_header (current_line ):
269
+ instack = True
270
+
271
+ if should_print_previous_line (current_line , previous_line ):
272
+ print_with_flush (previous_line .rstrip ())
273
+
274
+ if should_print_current_line (current_line , previous_line ):
275
+ print_with_flush (current_line .rstrip ())
276
+
277
+ if fatal_error_with_stack_trace_found (current_line , previous_line ):
278
+ fatal_error_header = previous_line
279
+ fatal_error_stack_trace_header = current_line
280
+
281
+ previous_line = current_line
282
+ if is_fatal_error (previous_line ):
283
+ print_with_flush (previous_line .rstrip ())
284
+ processed_stack = get_processed_stack (binary , dyn_libs , stack )
285
+ print_stack (fatal_error_header ,
286
+ fatal_error_stack_trace_header ,
287
+ fatal_log_format ,
288
+ processed_stack )
206
289
207
290
208
291
if __name__ == '__main__' :
0 commit comments