1 '''
2 Crash Information
3
4 Represents information about a crash. Specific subclasses implement
5 different crash data supported by the implementation.
6
7 @author: Christian Holler (:decoder)
8
9 @license:
10
11 This Source Code Form is subject to the terms of the Mozilla Public
12 License, v. 2.0. If a copy of the MPL was not distributed with this
13 file, You can obtain one at http://mozilla.org/MPL/2.0/.
14
15 @contact: choller@mozilla.com
16 '''
17
18
19 from __future__ import print_function
20
21 from abc import ABCMeta
22 import json
23 from numpy import int32, int64, uint32, uint64
24 import os
25 import re
26 import sys
27
28 from FTB import AssertionHelper
29 from FTB.ProgramConfiguration import ProgramConfiguration
30 from FTB.Signatures import RegisterHelper
31 from FTB.Signatures.CrashSignature import CrashSignature
35 '''
36 Abstract base class that provides a method to instantiate the right sub class.
37 It also supports generating a CrashSignature based on the stored information.
38 '''
39 __metaclass__ = ABCMeta
40
42
43 self.rawStdout = []
44 self.rawStderr = []
45 self.rawCrashData = []
46
47
48 self.backtrace = []
49 self.registers = {}
50 self.crashAddress = None
51 self.crashInstruction = None
52
53
54 self.configuration = None
55
56
57
58
59 self.testcase = None
60
61
62 self.failureReason = None
63
65 buf = []
66 buf.append("Crash trace:")
67 buf.append("")
68 for idx, frame in enumerate(self.backtrace):
69 buf.append("# %02d %s" % (idx, frame))
70 buf.append("")
71
72 if self.crashAddress:
73 buf.append("Crash address: %s" % self.crashAddress)
74
75 if self.crashInstruction:
76 buf.append("Crash instruction: %s" % self.crashInstruction)
77
78 if self.crashAddress or self.crashInstruction:
79 buf.append("")
80
81 buf.append("Last 5 lines on stderr:")
82 buf.extend(self.rawStderr[-5:])
83
84 return "\n".join(buf)
85
87 '''
88 Create a cache object for restoring the class instance later on without parsing
89 the crash data again. This object includes all class fields except for the
90 storage heavy raw objects like stdout, stderr and raw crashdata.
91
92 @rtype: dict
93 @return: Dictionary containing expensive class fields
94 '''
95 cacheObject = {}
96 cacheObject['backtrace'] = self.backtrace
97 cacheObject['registers'] = self.registers
98
99 if self.crashAddress is not None:
100 cacheObject['crashAddress'] = long(self.crashAddress)
101 else:
102 cacheObject['crashAddress'] = None
103
104 cacheObject['crashInstruction'] = self.crashInstruction
105 cacheObject['failureReason'] = self.failureReason
106
107 return cacheObject
108
109 @staticmethod
110 - def fromRawCrashData(stdout, stderr, configuration, auxCrashData=None, cacheObject=None):
111 '''
112 Create appropriate CrashInfo instance from raw crash data
113
114 @type stdout: List of strings
115 @param stdout: List of lines as they appeared on stdout
116 @type stderr: List of strings
117 @param stderr: List of lines as they appeared on stderr
118 @type configuration: ProgramConfiguration
119 @param configuration: Exact program configuration that is associated with the crash
120 @type auxCrashData: List of strings
121 @param auxCrashData: Optional additional crash output (e.g. GDB). If not specified, stderr is used.
122 @type cacheObject: Dictionary
123 @param cacheObject: The cache object that should be used to restore the class fields
124 instead of parsing the crash data. The appropriate object can be
125 created by calling the toCacheObject method.
126
127 @rtype: CrashInfo
128 @return: Crash information object
129 '''
130
131 assert stdout == None or isinstance(stdout, list) or isinstance(stdout, basestring)
132 assert stderr == None or isinstance(stderr, list) or isinstance(stderr, basestring)
133 assert auxCrashData == None or isinstance(auxCrashData, list) or isinstance(auxCrashData, basestring)
134
135 assert isinstance(configuration, ProgramConfiguration)
136
137 if isinstance(stdout, basestring):
138 stdout = stdout.splitlines()
139
140 if isinstance(stderr, basestring):
141 stderr = stderr.splitlines()
142
143 if isinstance(auxCrashData, basestring):
144 auxCrashData = auxCrashData.splitlines()
145
146 if cacheObject is not None:
147 c = CrashInfo()
148
149 if stdout != None:
150 c.rawStdout.extend(stdout)
151
152 if stderr != None:
153 c.rawStderr.extend(stderr)
154
155 if auxCrashData != None:
156 c.rawCrashData.extend(auxCrashData)
157
158 c.configuration = configuration
159 c.backtrace = cacheObject['backtrace']
160 c.registers = cacheObject['registers']
161 c.crashAddress = cacheObject['crashAddress']
162 c.crashInstruction = cacheObject['crashInstruction']
163 c.failureReason = cacheObject['failureReason']
164
165 return c
166
167 asanString = "ERROR: AddressSanitizer:"
168 gdbString = "Program received signal "
169 gdbCoreString = "Program terminated with signal "
170 ubsanString = "SUMMARY: AddressSanitizer: undefined-behavior"
171 appleString = "OS Version: Mac OS X"
172
173
174 minidumpFirstString = "OS|"
175 minidumpSecondString = "CPU|"
176 minidumpFirstDetected = False
177
178
179 lines = []
180 if auxCrashData != None:
181 lines.extend(auxCrashData)
182 if stderr != None:
183 lines.extend(stderr)
184
185 for line in lines:
186 if ubsanString in line:
187 return UBSanCrashInfo(stdout, stderr, configuration, auxCrashData)
188 elif asanString in line:
189 return ASanCrashInfo(stdout, stderr, configuration, auxCrashData)
190 elif appleString in line:
191 return AppleCrashInfo(stdout, stderr, configuration, auxCrashData)
192 elif gdbString in line or gdbCoreString in line:
193 return GDBCrashInfo(stdout, stderr, configuration, auxCrashData)
194 elif not minidumpFirstDetected and minidumpFirstString in line:
195
196
197 minidumpFirstDetected = True
198 elif minidumpFirstDetected and minidumpSecondString in line:
199 return MinidumpCrashInfo(stdout, stderr, configuration, auxCrashData)
200 elif minidumpFirstDetected:
201 minidumpFirstDetected = False
202
203
204
205
206 return NoCrashInfo(stdout, stderr, configuration, auxCrashData)
207
209 '''
210 @rtype: String
211 @return: A string representing this crash (short signature)
212 '''
213
214 abortMsg = AssertionHelper.getAssertion(self.rawStderr, True)
215 if abortMsg != None:
216 return abortMsg
217
218
219 if self.rawCrashData:
220 abortMsg = AssertionHelper.getAssertion(self.rawCrashData, True)
221 if abortMsg != None:
222 return abortMsg
223
224 if not len(self.backtrace):
225 return "No crash detected"
226
227 return "[@ %s]" % self.backtrace[0]
228
229 - def createCrashSignature(self, forceCrashAddress=False, forceCrashInstruction=False, maxFrames=8, minimumSupportedVersion=13):
230 '''
231 @param forceCrashAddress: If True, the crash address will be included in any case
232 @type forceCrashAddress: bool
233 @param forceCrashInstruction: If True, the crash instruction will be included in any case
234 @type forceCrashInstruction: bool
235 @param maxFrames: How many frames (at most) should be included in the signature
236 @type maxFrames: int
237
238 @param minimumSupportedVersion: The minimum crash signature standard version that the
239 generated signature should be valid for (10 => 1.0, 13 => 1.3)
240 @type minimumSupportedVersion: int
241
242 @rtype: CrashSignature
243 @return: A crash signature object
244 '''
245
246 if len(self.backtrace) > maxFrames:
247 numFrames = maxFrames
248 else:
249 numFrames = len(self.backtrace)
250
251
252 abortMsg = AssertionHelper.getAssertion(self.rawStderr, True)
253 abortMsgInCrashdata = False
254
255 if abortMsg is None and minimumSupportedVersion >= 13:
256
257
258
259 abortMsg = AssertionHelper.getAssertion(self.rawCrashData, True)
260 abortMsgInCrashdata = True
261
262 if abortMsg != None:
263 abortMsg = AssertionHelper.getSanitizedAssertionPattern(abortMsg)
264
265
266 topStackLimit = 4
267
268 symptomArr = []
269
270 if abortMsg != None:
271 abortMsgSrc = "stderr"
272 if abortMsgInCrashdata:
273 abortMsgSrc = "crashdata"
274
275
276
277
278
279 if minimumSupportedVersion < 12:
280 stringObj = { "value" : abortMsg, "matchType" : "pcre" }
281 symptomObj = { "type" : "output", "src" : abortMsgSrc, "value" : stringObj }
282 else:
283 symptomObj = { "type" : "output", "src" : abortMsgSrc, "value" : "/%s/" % abortMsg }
284 symptomArr.append(symptomObj)
285
286
287
288
289
290
291 if numFrames >= topStackLimit:
292 topStackMissCount = 0
293 else:
294 topStackMissCount = topStackLimit - numFrames
295
296
297
298 if minimumSupportedVersion < 12:
299 for idx in range(0, numFrames):
300 functionName = self.backtrace[idx]
301 if not functionName == "??":
302 symptomObj = { "type" : "stackFrame", "frameNumber" : idx, "functionName" : functionName }
303 symptomArr.append(symptomObj)
304 elif idx < 4:
305
306 topStackMissCount += 1
307 else:
308 framesArray = []
309
310 for idx in range(0, numFrames):
311 functionName = self.backtrace[idx]
312 if not functionName == "??":
313 framesArray.append(functionName)
314 else:
315 framesArray.append("?")
316 if idx < 4:
317
318 topStackMissCount += 1
319
320 lastSymbolizedFrame = None
321 for frameIdx in range(0, len(framesArray)):
322 if str(framesArray[frameIdx]) != '?':
323 lastSymbolizedFrame = frameIdx
324
325 if lastSymbolizedFrame != None:
326
327 framesArray = framesArray[:lastSymbolizedFrame + 1]
328 else:
329
330
331 framesArray = []
332
333 if framesArray:
334 symptomArr.append({ "type" : "stackFrames", "functionNames" : framesArray })
335
336
337 stackIsInsufficient = topStackMissCount >= 2 and abortMsg == None
338
339 includeCrashAddress = stackIsInsufficient or forceCrashAddress
340 includeCrashInstruction = (stackIsInsufficient and self.crashInstruction != None) or forceCrashInstruction
341
342 if includeCrashAddress:
343 if self.crashAddress == None:
344 crashAddress = ""
345 elif self.crashAddress != 0L and self.crashAddress < 0x100L:
346
347
348 crashAddress = "< 0x100"
349 else:
350 crashAddress = hex(self.crashAddress).rstrip("L")
351
352 crashAddressSymptomObj = { "type" : "crashAddress", "address" : crashAddress }
353 symptomArr.append(crashAddressSymptomObj)
354
355 if includeCrashInstruction:
356 if self.crashInstruction == None:
357 failureReason = self.failureReason
358 self.failureReason = "No crash instruction available from crash data. Reason: %s" % failureReason
359 return None
360
361 crashInstructionSymptomObj = { "type" : "instruction", "instructionName" : self.crashInstruction }
362 symptomArr.append(crashInstructionSymptomObj)
363
364 sigObj = { "symptoms" : symptomArr }
365
366 return CrashSignature(json.dumps(sigObj, indent=2))
367
368 @staticmethod
370 '''
371 This function removes function arguments and other non-generic parts
372 of the function frame, returning a (hopefully) generic function name.
373
374 @param frame: The stack frame to sanitize
375 @type forceCrashAddress: str
376
377 @rtype: str
378 @return: Sanitized stack frame
379 '''
380
381
382 if frame.endswith(" const"):
383 frame = frame[0:-len(" const")]
384
385
386 if frame.endswith(")"):
387 idx = len(frame) - 2
388 parlevel = 0
389 while(idx > 0):
390 if frame[idx] == "(":
391 if parlevel > 0:
392 parlevel -= 1
393 else:
394
395 break
396 elif frame[idx] == ")":
397 parlevel += 1
398
399 idx -= 1
400
401
402
403 if idx:
404 frame = frame[:idx]
405
406 if "lambda" in frame:
407 frame = re.sub("<lambda at .+?:\d+:\d+>", "", frame)
408
409 return frame
410
412 - def __init__(self, stdout, stderr, configuration, crashData=None):
413 '''
414 Private constructor, called by L{CrashInfo.fromRawCrashData}. Do not use directly.
415 '''
416 CrashInfo.__init__(self)
417
418 if stdout != None:
419 self.rawStdout.extend(stdout)
420
421 if stderr != None:
422 self.rawStderr.extend(stderr)
423
424 if crashData != None:
425 self.rawCrashData.extend(crashData)
426
427 self.configuration = configuration
428
431 - def __init__(self, stdout, stderr, configuration, crashData=None):
432 '''
433 Private constructor, called by L{CrashInfo.fromRawCrashData}. Do not use directly.
434 '''
435 CrashInfo.__init__(self)
436
437 if stdout != None:
438 self.rawStdout.extend(stdout)
439
440 if stderr != None:
441 self.rawStderr.extend(stderr)
442
443 if crashData != None:
444 self.rawCrashData.extend(crashData)
445
446 self.configuration = configuration
447
448
449 if crashData:
450 asanOutput = crashData
451 else:
452 asanOutput = stderr
453
454
455 asanMessages = [
456 "on address",
457 "on unknown address",
458 "double-free on",
459 "not malloc\\(\\)\\-ed:",
460 "not owned:"
461 ]
462
463
464
465 asanCrashAddressPattern = " AddressSanitizer:.+ (?:" + "|".join(asanMessages) + ")\\s+0x([0-9a-f]+)"
466 asanRegisterPattern = "(?:\\s+|\\()pc\\s+0x([0-9a-f]+)\\s+(sp|bp)\\s+0x([0-9a-f]+)\\s+(sp|bp)\\s+0x([0-9a-f]+)"
467
468 expectedIndex = 0
469 for traceLine in asanOutput:
470 if self.crashAddress == None:
471 match = re.search(asanCrashAddressPattern, traceLine)
472
473 if match != None:
474 self.crashAddress = long(match.group(1), 16)
475
476
477 match = re.search(asanRegisterPattern, traceLine)
478 if match != None:
479 self.registers["pc"] = long(match.group(1), 16)
480 self.registers[match.group(2)] = long(match.group(3), 16)
481 self.registers[match.group(4)] = long(match.group(5), 16)
482 else:
483 raise RuntimeError("Fatal error parsing ASan trace: Failed to isolate registers in line: %s" % traceLine)
484
485 parts = traceLine.strip().split()
486
487
488 if not parts or not parts[0].startswith("#"):
489 continue
490
491 index = int(parts[0][1:])
492
493
494 if index == 0:
495 expectedIndex = 0
496
497 if not expectedIndex == index:
498 raise RuntimeError("Fatal error parsing ASan trace (Index mismatch, got index %s but expected %s)" % (index, expectedIndex))
499
500 component = None
501 if len(parts) > 2:
502 if parts[2] == "in":
503 component = " ".join(parts[3:-1])
504 else:
505
506 component = parts[2][1:-1]
507 else:
508 print("Warning: Missing component in this line: %s" % traceLine, file=sys.stderr)
509 component = "<missing>"
510
511 self.backtrace.append(CrashInfo.sanitizeStackFrame(component))
512 expectedIndex += 1
513
514 if not self.backtrace and self.crashAddress != None:
515
516
517
518 self.backtrace.append("??")
519
521 - def __init__(self, stdout, stderr, configuration, crashData=None):
522 '''
523 Private constructor, called by L{CrashInfo.fromRawCrashData}. Do not use directly.
524 '''
525 CrashInfo.__init__(self)
526
527 if stdout != None:
528 self.rawStdout.extend(stdout)
529
530 if stderr != None:
531 self.rawStderr.extend(stderr)
532
533 if crashData != None:
534 self.rawCrashData.extend(crashData)
535
536 self.configuration = configuration
537
538
539 if crashData:
540 ubsanOutput = crashData
541 else:
542 ubsanOutput = stderr
543
544 ubsanErrorPattern = ":\\d+:\\d+: runtime error:"
545 ubsanPatternSeen = False
546
547 expectedIndex = 0
548 for traceLine in ubsanOutput:
549 if re.match(ubsanErrorPattern, traceLine) != None:
550 ubsanPatternSeen = True
551
552 parts = traceLine.strip().split()
553
554
555 if not parts or not parts[0].startswith("#"):
556 continue
557
558 index = int(parts[0][1:])
559
560 if not expectedIndex == index:
561 raise RuntimeError("Fatal error parsing UBSan trace (Index mismatch, got index %s but expected %s)" % (index, expectedIndex))
562
563 component = None
564 if len(parts) > 2:
565 if parts[2] == "in":
566 component = " ".join(parts[3:-1])
567 else:
568
569 component = parts[2][1:-1]
570 else:
571 print("Warning: Missing component in this line: %s" % traceLine, file=sys.stderr)
572 component = "<missing>"
573
574 self.backtrace.append(CrashInfo.sanitizeStackFrame(component))
575 expectedIndex += 1
576
577 if not self.backtrace and ubsanPatternSeen:
578
579
580
581 self.backtrace.append("??")
582
584 - def __init__(self, stdout, stderr, configuration, crashData=None):
585 '''
586 Private constructor, called by L{CrashInfo.fromRawCrashData}. Do not use directly.
587 '''
588 CrashInfo.__init__(self)
589
590 if stdout != None:
591 self.rawStdout.extend(stdout)
592
593 if stderr != None:
594 self.rawStderr.extend(stderr)
595
596 if crashData != None:
597 self.rawCrashData.extend(crashData)
598
599 self.configuration = configuration
600
601
602 if crashData:
603 gdbOutput = crashData
604 else:
605 gdbOutput = stderr
606
607 gdbFramePatterns = [
608 "\\s*#(\\d+)\\s+(0x[0-9a-f]+) in (.+?) \\(.*?\\)( at .+)?",
609 "\\s*#(\\d+)\\s+()(.+?) \\(.*?\\)( at .+)?"
610 ]
611
612 gdbRegisterPattern = RegisterHelper.getRegisterPattern() + "\\s+0x([0-9a-f]+)"
613 gdbCrashAddressPattern = "Crash Address:\\s+0x([0-9a-f]+)"
614 gdbCrashInstructionPattern = "=> 0x[0-9a-f]+(?: <.+>)?:\\s+(.*)"
615
616 lastLineBuf = ""
617
618 pastFrames = False
619
620 for traceLine in gdbOutput:
621
622
623
624
625 if not pastFrames and re.match("\\s*#\\d+.+", lastLineBuf) != None and re.match("\\s*#\\d+.+", traceLine) != None:
626 print("Fatal error parsing this GDB trace line:", file=sys.stderr)
627 print(lastLineBuf, file=sys.stderr)
628 raise RuntimeError("Fatal error parsing GDB trace")
629
630 if not len(lastLineBuf):
631 match = re.search(gdbRegisterPattern, traceLine)
632 if match != None:
633 pastFrames = True
634 register = match.group(1)
635 value = long(match.group(2), 16)
636 self.registers[register] = value
637 else:
638 match = re.search(gdbCrashAddressPattern, traceLine)
639 if match != None:
640 self.crashAddress = long(match.group(1), 16)
641 else:
642 match = re.search(gdbCrashInstructionPattern, traceLine)
643 if match != None:
644 self.crashInstruction = match.group(1)
645
646 if not pastFrames:
647 if not len(lastLineBuf) and re.match("\\s*#\\d+.+", traceLine) == None:
648
649 continue
650
651 lastLineBuf += traceLine
652
653 functionName = None
654 frameIndex = None
655
656 for gdbPattern in gdbFramePatterns:
657 match = re.search(gdbPattern, lastLineBuf)
658 if match != None:
659 frameIndex = int(match.group(1))
660 functionName = match.group(3)
661 break
662
663 if frameIndex == None:
664
665 continue
666 else:
667
668 lastLineBuf = ""
669
670
671 if len(self.backtrace) != frameIndex and frameIndex == 0:
672 self.backtrace.pop(0)
673 elif len(self.backtrace) != frameIndex:
674 print("Fatal error parsing this GDB trace (Index mismatch, wanted %s got %s ): " % (len(self.backtrace), frameIndex), file=sys.stderr)
675 print(os.linesep.join(gdbOutput), file=sys.stderr)
676 raise RuntimeError("Fatal error parsing GDB trace")
677
678
679
680
681 gdbErrorIdx = functionName.find(" (/build/buildd/gdb")
682 if gdbErrorIdx > 0:
683 functionName = functionName[:gdbErrorIdx]
684
685 self.backtrace.append(functionName)
686
687
688 if self.crashAddress == None and self.crashInstruction != None:
689 crashAddress = GDBCrashInfo.calculateCrashAddress(self.crashInstruction, self.registers)
690
691 if isinstance(crashAddress, basestring):
692 self.failureReason = crashAddress
693 return
694
695 self.crashAddress = crashAddress
696
697 if self.crashAddress != None and self.crashAddress < 0:
698 if RegisterHelper.getBitWidth(self.registers) == 32:
699 self.crashAddress = uint32(self.crashAddress)
700 else:
701
702 self.crashAddress = uint64(self.crashAddress)
703
704 @staticmethod
706 '''
707 Calculate the crash address given the crash instruction and register contents
708
709 @type crashInstruction: string
710 @param crashInstruction: Crash instruction string as provided by GDB
711 @type registerMap: Map from string to long
712 @param registerMap: Map of register names to values
713
714 @rtype: long
715 @return The calculated crash address
716
717 On error, a string containing the failure message is returned instead.
718 '''
719
720 if (len(crashInstruction) == 0):
721
722
723
724 return RegisterHelper.getInstructionPointer(registerMap)
725
726 parts = crashInstruction.split(None, 1)
727
728 if len(parts) != 2:
729 raise RuntimeError("Failed to split instruction and operands apart: %s" % crashInstruction)
730
731 instruction = parts[0]
732 operands = parts[1]
733
734 if not re.match("[a-z\\.]+", instruction):
735 raise RuntimeError("Invalid instruction: %s" % instruction)
736
737 parts = operands.split(",")
738
739
740
741
742
743
744
745
746
747
748
749
750
751 failureReason = "Unknown failure."
752
753 if RegisterHelper.isX86Compatible(registerMap):
754 if len(parts) == 1:
755 if instruction == "callq" or instruction == "push" or instruction == "pop":
756 return RegisterHelper.getStackPointer(registerMap)
757 else:
758 failureReason = "Unsupported single-operand instruction."
759 elif len(parts) == 2:
760 failureReason = "Unknown failure with two-operand instruction."
761 derefOp = None
762 if "(" in parts[0] and ")" in parts[0]:
763 derefOp = parts[0]
764
765 if "(" in parts[1] and ")" in parts[1]:
766 if derefOp != None:
767 if ":(" in parts[1]:
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788 return None
789
790 raise RuntimeError("Instruction operands have multiple loads? %s" % crashInstruction)
791
792 derefOp = parts[1]
793
794 if derefOp != None:
795 match = re.match("((?:\\-?0x[0-9a-f]+)?)\\(%([a-z0-9]+)\\)", derefOp)
796 if match != None:
797 offset = 0L
798 if len(match.group(1)):
799 offset = long(match.group(1), 16)
800
801 val = RegisterHelper.getRegisterValue(match.group(2), registerMap)
802
803
804 if val == None:
805 failureReason = "Missing value for register %s " % match.group(2)
806 else:
807 if RegisterHelper.getBitWidth(registerMap) == 32:
808 return long(int32(uint32(offset)) + int32(uint32(val)))
809 else:
810
811 return long(int64(uint64(offset)) + int64(uint64(val)))
812 else:
813 failureReason = "Failed to decode two-operand instruction: No dereference operation or hardcoded address detected."
814
815
816
817
818
819
820
821
822 for x in (parts[1], parts[0]):
823 result = re.match("\\$?(\\-?0x[0-9a-f]+)", x)
824 if result != None:
825 return long(result.group(1), 16)
826 elif len(parts) == 3:
827
828 if "(" in parts[0] and ")" in parts[2]:
829 complexDerefOp = parts[0] + "," + parts[1] + "," + parts[2]
830
831 (result, reason) = GDBCrashInfo.calculateComplexDerefOpAddress(complexDerefOp, registerMap)
832
833 if result == None:
834 failureReason = reason
835 else:
836 return result
837 else:
838 raise RuntimeError("Unexpected instruction pattern: %s" % crashInstruction)
839 elif len(parts) == 4:
840 if "(" in parts[0] and not ")" in parts[0]:
841 complexDerefOp = parts[0] + "," + parts[1] + "," + parts[2]
842 elif not "(" in parts[0] and not ")" in parts[0]:
843 complexDerefOp = parts[1] + "," + parts[2] + "," + parts[3]
844
845 (result, reason) = GDBCrashInfo.calculateComplexDerefOpAddress(complexDerefOp, registerMap)
846
847 if result == None:
848 failureReason = reason
849 else:
850 return result
851 else:
852 raise RuntimeError("Unexpected length after splitting operands of this instruction: %s" % crashInstruction)
853 else:
854 failureReason = "Architecture is not supported."
855
856 print("Unable to calculate crash address from instruction: %s " % crashInstruction, file=sys.stderr)
857 print("Reason: %s" % failureReason, file=sys.stderr)
858 return failureReason
859
860 @staticmethod
862
863 match = re.match("((?:\\-?0x[0-9a-f]+)?)\\(%([a-z0-9]+),%([a-z0-9]+),([0-9]+)\\)", complexDerefOp)
864 if match != None:
865 offset = 0L
866 if len(match.group(1)) > 0:
867 offset = long(match.group(1), 16)
868
869 regA = RegisterHelper.getRegisterValue(match.group(2), registerMap)
870 regB = RegisterHelper.getRegisterValue(match.group(3), registerMap)
871
872 mult = long(match.group(4), 16)
873
874
875 if regA == None or regB == None:
876 if regA == None:
877 return (None, "Missing value for register %s" % match.group(2))
878 else:
879 return (None, "Missing value for register %s" % match.group(3))
880
881 if RegisterHelper.getBitWidth(registerMap) == 32:
882 val = int32(uint32(regA)) + int32(uint32(offset)) + (int32(uint32(regB)) * int32(uint32(mult)))
883 else:
884
885 val = int64(uint64(regA)) + int64(uint64(offset)) + (int64(uint64(regB)) * int64(uint64(mult)))
886 return (long(val), None)
887
888 return (None, "Unknown failure.")
889
892 - def __init__(self, stdout, stderr, configuration, crashData=None):
893 '''
894 Private constructor, called by L{CrashInfo.fromRawCrashData}. Do not use directly.
895 '''
896 CrashInfo.__init__(self)
897
898 if stdout != None:
899 self.rawStdout.extend(stdout)
900
901 if stderr != None:
902 self.rawStderr.extend(stderr)
903
904 if crashData != None:
905 self.rawCrashData.extend(crashData)
906
907 self.configuration = configuration
908
909
910 if crashData:
911 minidumpOuput = crashData
912 else:
913 minidumpOuput = stderr
914
915 crashThread = None
916 for traceLine in minidumpOuput:
917 if crashThread != None:
918 if traceLine.startswith("%s|" % crashThread):
919 components = traceLine.split("|")
920
921
922 if not len(components[3]):
923 self.backtrace.append("??")
924 else:
925 self.backtrace.append(CrashInfo.sanitizeStackFrame(components[3]))
926 elif traceLine.startswith("Crash|"):
927 components = traceLine.split("|")
928 crashThread = int(components[3])
929 self.crashAddress = long(components[2], 16)
930
933 - def __init__(self, stdout, stderr, configuration, crashData=None):
934 '''
935 Private constructor, called by L{CrashInfo.fromRawCrashData}. Do not use directly.
936 '''
937 CrashInfo.__init__(self)
938
939 if stdout != None:
940 self.rawStdout.extend(stdout)
941
942 if stderr != None:
943 self.rawStderr.extend(stderr)
944
945 if crashData != None:
946 self.rawCrashData.extend(crashData)
947
948 self.configuration = configuration
949
950 inCrashingThread = False
951 for line in crashData:
952
953 if line.startswith("Exception Codes:"):
954
955
956
957 address = line.split(" ")[-1]
958 if address.startswith('0x'):
959 self.crashAddress = long(address, 16)
960
961
962 if re.match(r'Thread \d+ Crashed:', line):
963 inCrashingThread = True
964 continue
965
966 if line.strip() == "":
967 inCrashingThread = False
968 continue
969
970 if inCrashingThread:
971
972
973 components = line.split(None, 3)
974 stackEntry = components[3]
975 if stackEntry.startswith('0'):
976 self.backtrace.append("??")
977 else:
978 stackEntry = AppleCrashInfo.removeFilename(stackEntry)
979 stackEntry = AppleCrashInfo.removeOffset(stackEntry)
980 stackEntry = CrashInfo.sanitizeStackFrame(stackEntry)
981 self.backtrace.append(stackEntry)
982
983 @staticmethod
985 match = re.match(r'(.*) \(\S+\)', stackEntry)
986 if match:
987 return match.group(1)
988 return stackEntry
989
990 @staticmethod
992 match = re.match(r'(.*) \+ \d+', stackEntry)
993 if match:
994 return match.group(1)
995 return stackEntry
996