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
@@ -40,6 +42,11 @@ except ImportError:
40
42
sys .path .append (site_packages )
41
43
import lldb
42
44
45
+ lldb_target = None
46
+ known_memmap = {}
47
+ def print_with_flush (buff ):
48
+ print (buff )
49
+ sys .stdout .flush ()
43
50
44
51
def process_ldd (lddoutput ):
45
52
dyn_libs = {}
@@ -53,29 +60,18 @@ def process_ldd(lddoutput):
53
60
return dyn_libs
54
61
55
62
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 )
63
+ def setup_lldb_target (binary , memmap ):
64
+ global lldb_target
65
+ if not lldb_target :
66
+ lldb_debugger = lldb .SBDebugger .Create ()
67
+ lldb_target = lldb_debugger .CreateTarget (binary )
68
+ module = lldb_target .GetModuleAtIndex (0 )
60
69
for dynlib_path in memmap :
61
70
module = lldb_target .AddModule (
62
71
dynlib_path , lldb .LLDB_ARCH_DEFAULT , None , None )
63
72
text_section = module .FindSection (".text" )
64
73
slide = text_section .GetFileAddress () - text_section .GetFileOffset ()
65
74
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
-
79
75
80
76
def check_base_address (dynlib_path , dynlib_baseaddr , memmap ):
81
77
global known_memmap
@@ -91,7 +87,6 @@ def check_base_address(dynlib_path, dynlib_baseaddr, memmap):
91
87
dynlib_path , existing_baseaddr , dynlib_baseaddr )
92
88
raise Exception (error_msg )
93
89
94
-
95
90
def symbolicate_one (frame_addr , frame_idx , dynlib_fname ):
96
91
global lldb_target
97
92
so_addr = lldb_target .ResolveLoadAddress (frame_addr - 1 )
@@ -115,12 +110,12 @@ def symbolicate_one(frame_addr, frame_idx, dynlib_fname):
115
110
return "{0:s} {1:s} {2:s}" .format (
116
111
frame_fragment , symbol_fragment , line_fragment )
117
112
118
-
119
- def process_stack (binary , dyn_libs , stack ):
113
+ def get_processed_stack (binary , dyn_libs , stack ):
120
114
global lldb_target
121
115
global known_memmap
116
+ processed_stack = []
122
117
if len (stack ) == 0 :
123
- return
118
+ return processed_stack
124
119
memmap = {}
125
120
full_stack = []
126
121
for line in stack :
@@ -150,21 +145,51 @@ def process_stack(binary, dyn_libs, stack):
150
145
full_stack .append (
151
146
{"line" : line , "framePC" : framePC , "dynlib_fname" : dynlib_fname })
152
147
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 :
148
+ setup_lldb_target (binary , memmap )
149
+
150
+ for frame_idx , frame in enumerate (full_stack ):
159
151
frame_addr = frame ["framePC" ]
160
152
dynlib_fname = frame ["dynlib_fname" ]
161
153
try :
162
154
sym_line = symbolicate_one (frame_addr , frame_idx , dynlib_fname )
163
- print (sym_line )
155
+ processed_stack . append (sym_line )
164
156
except Exception :
165
- print (frame ["line" ].rstrip ())
166
- frame_idx = frame_idx + 1
157
+ processed_stack .append (frame ["line" ].rstrip ())
158
+
159
+ return processed_stack
160
+
161
+ def is_fatal_error (line ):
162
+ return line .startswith ("Fatal error:" )
163
+
164
+ def is_stack_trace_header (line ):
165
+ return line .startswith ("Current stack trace:" )
166
+
167
+ def should_print_previous_line (current_line , previous_line ):
168
+ return is_fatal_error (previous_line ) and not is_stack_trace_header (current_line )
169
+
170
+ def should_print_current_line (current_line , previous_line ):
171
+ return (not is_fatal_error (current_line ) and not is_stack_trace_header (current_line )) or (is_stack_trace_header (current_line ) and not is_fatal_error (previous_line ))
172
+
173
+ def fatal_error_with_stack_trace_found (current_line , previous_line ):
174
+ return is_stack_trace_header (current_line ) and is_fatal_error (previous_line )
175
+
176
+ def print_stack (fatal_error_header , fatal_error_stack_trace_header , fatal_log_format , processed_stack ):
177
+ if not fatal_error_header :
178
+ for line in processed_stack :
179
+ print_with_flush (line )
180
+ else :
181
+ #fatal error with a stack trace
182
+ stack_str = fatal_error_header + fatal_error_stack_trace_header + '\n ' .join (processed_stack )
183
+ formatted_output = fatal_log_format
184
+
185
+ if "%t" in formatted_output :
186
+ current_time = datetime .datetime .now ()
187
+ time_in_iso_format = current_time .strftime ('%Y-%m-%dT%H:%M:%S,%f%z' )
188
+ formatted_output = formatted_output .replace ("%t" , time_in_iso_format )
189
+ if "%m" in formatted_output :
190
+ formatted_output = formatted_output .replace ("%m" , stack_str )
167
191
192
+ print_with_flush (formatted_output )
168
193
169
194
def main ():
170
195
parser = argparse .ArgumentParser (
@@ -175,35 +200,60 @@ def main():
175
200
parser .add_argument (
176
201
"log" , nargs = '?' , type = argparse .FileType ("rU" ), default = "-" ,
177
202
help = "Log file for symbolication. Defaults to stdin." )
203
+ parser .add_argument (
204
+ "--fatal-log-format" , default = "%m" ,
205
+ help = "Format for logging fatal errors. Variable %%t will be replaced with current time in ISO 8601 format, variable %%m will be replaced with the error message with a full stack trace." )
178
206
args = parser .parse_args ()
179
207
180
208
binary = args .binary
209
+ fatal_log_format = args .fatal_log_format
181
210
182
211
lddoutput = subprocess .check_output (
183
212
['ldd' , binary ], stderr = subprocess .STDOUT )
184
213
dyn_libs = process_ldd (lddoutput )
185
214
186
215
instack = False
216
+ previous_line = ""
187
217
stackidx = 0
188
218
stack = []
219
+ fatal_error_header = ""
220
+ fatal_error_stack_trace_header = ""
189
221
190
222
while True :
191
- line = args .log .readline ()
192
- if not line :
223
+ current_line = args .log .readline ()
224
+ if not current_line :
193
225
break
194
- if instack and line .startswith (str (stackidx )):
195
- stack .append (line )
226
+ if instack and current_line .startswith (str (stackidx )):
227
+ stack .append (current_line )
196
228
stackidx = stackidx + 1
197
229
else :
230
+ processed_stack = get_processed_stack (binary , dyn_libs , stack )
231
+ print_stack (fatal_error_header , fatal_error_stack_trace_header , fatal_log_format , processed_stack )
232
+
198
233
instack = False
199
234
stackidx = 0
200
- process_stack (binary , dyn_libs , stack )
201
235
stack = []
202
- print (line .rstrip ())
203
- if line .startswith ("Current stack trace:" ):
204
- instack = True
205
- process_stack (binary , dyn_libs , stack )
236
+ fatal_error_header = ""
237
+ fatal_error_stack_trace_header = ""
238
+
239
+ if is_stack_trace_header (current_line ):
240
+ instack = True
241
+
242
+ if should_print_previous_line (current_line , previous_line ):
243
+ print_with_flush (previous_line .rstrip ())
244
+
245
+ if should_print_current_line (current_line , previous_line ):
246
+ print_with_flush (current_line .rstrip ())
247
+
248
+ if fatal_error_with_stack_trace_found (current_line , previous_line ):
249
+ fatal_error_header = previous_line
250
+ fatal_error_stack_trace_header = current_line
206
251
252
+ previous_line = current_line
253
+ if is_fatal_error (previous_line ):
254
+ print_with_flush (previous_line .rstrip ())
255
+ processed_stack = get_processed_stack (binary , dyn_libs , stack )
256
+ print_stack (fatal_error_header , fatal_error_stack_trace_header , fatal_log_format , processed_stack )
207
257
208
258
if __name__ == '__main__' :
209
259
main ()
0 commit comments