Currently, in macOS, when a process crashes, lldb halts inside the
implementation disassembly without yielding any useful information.
The only way to get more information is to detach from the process, then wait
for ReportCrash to generate a report, find the report, then see what error
message was included in it. Instead of waiting for this to happen, lldb could
locate the error_string and make it available to the user.
This patch addresses this issue by enabling the user to fetch extended
crash information for crashed processes using `process status --verbose`.
Depending on the platform, this will try to gather different crash information
into an structured data dictionnary. This dictionnary is generic and extensible,
as it contains an array for each different type of crash information.
On Darwin Platforms, lldb will iterate over each of the target's images,
extract their `__crash_info` section and generated a StructuredData::Array
containing, in each entry, the module spec, its UUID, the crash messages
and the abort cause. The array will be inserted into the platform's
`m_extended_crash_info` dictionnary and `FetchExtendedCrashInformation` will
return its JSON representation like this:
```
{
"crash-info annotations": [
{
"abort-cause": 0,
"image": "/usr/lib/system/libsystem_malloc.dylib",
"message": "main(76483,0x1000cedc0) malloc: *** error for object 0x1003040a0: pointer being freed was not allocated",
"message2": "",
"uuid": "5747D0C9-900D-3306-8D70-1E2EA4B7E821"
},
...
],
...
}
```
This crash information can also be fetched using the SB API or lldb-rpc protocol
using SBTarget::GetExtendedCrashInformation().
rdar://37736535
Differential Revision: https://reviews.llvm.org/D74657
Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
98 lines
3.0 KiB
Python
98 lines
3.0 KiB
Python
"""
|
|
Test lldb process crash info.
|
|
"""
|
|
|
|
import os
|
|
|
|
import lldb
|
|
from lldbsuite.test.decorators import *
|
|
from lldbsuite.test.lldbtest import *
|
|
from lldbsuite.test import lldbutil
|
|
|
|
class PlatformProcessCrashInfoTestCase(TestBase):
|
|
|
|
mydir = TestBase.compute_mydir(__file__)
|
|
|
|
def setUp(self):
|
|
TestBase.setUp(self)
|
|
self.runCmd("settings set auto-confirm true")
|
|
self.source = "main.c"
|
|
self.line = 3
|
|
|
|
def tearDown(self):
|
|
self.runCmd("settings clear auto-confirm")
|
|
TestBase.tearDown(self)
|
|
|
|
@skipUnlessDarwin
|
|
def test_cli(self):
|
|
"""Test that `process status --verbose` fetches the extended crash
|
|
information dictionnary from the command-line properly."""
|
|
self.build()
|
|
exe = self.getBuildArtifact("a.out")
|
|
self.expect("file " + exe,
|
|
patterns=["Current executable set to .*a.out"])
|
|
|
|
self.expect('process launch',
|
|
patterns=["Process .* launched: .*a.out"])
|
|
|
|
self.expect('process status --verbose',
|
|
patterns=["\"message\".*pointer being freed was not allocated"])
|
|
|
|
|
|
@skipUnlessDarwin
|
|
def test_api(self):
|
|
"""Test that lldb can fetch a crashed process' extended crash information
|
|
dictionnary from the api properly."""
|
|
self.build()
|
|
target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
|
|
self.assertTrue(target, VALID_TARGET)
|
|
|
|
target.LaunchSimple(None, None, os.getcwd())
|
|
|
|
stream = lldb.SBStream()
|
|
self.assertTrue(stream)
|
|
|
|
crash_info = target.GetExtendedCrashInformation()
|
|
|
|
error = crash_info.GetAsJSON(stream)
|
|
|
|
self.assertTrue(error.Success())
|
|
|
|
self.assertTrue(crash_info.IsValid())
|
|
|
|
self.assertIn("pointer being freed was not allocated", stream.GetData())
|
|
|
|
@skipUnlessDarwin
|
|
def test_before_launch(self):
|
|
"""Test that lldb doesn't fetch the extended crash information
|
|
dictionnary from if the process wasn't launched yet."""
|
|
self.build()
|
|
target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
|
|
self.assertTrue(target, VALID_TARGET)
|
|
|
|
stream = lldb.SBStream()
|
|
self.assertTrue(stream)
|
|
|
|
crash_info = target.GetExtendedCrashInformation()
|
|
|
|
error = crash_info.GetAsJSON(stream)
|
|
self.assertFalse(error.Success())
|
|
self.assertIn("No structured data.", error.GetCString())
|
|
|
|
@skipUnlessDarwin
|
|
def test_on_sane_process(self):
|
|
"""Test that lldb doesn't fetch the extended crash information
|
|
dictionnary from a 'sane' stopped process."""
|
|
self.build()
|
|
target, _, _, _ = lldbutil.run_to_line_breakpoint(self, lldb.SBFileSpec(self.source),
|
|
self.line)
|
|
|
|
stream = lldb.SBStream()
|
|
self.assertTrue(stream)
|
|
|
|
crash_info = target.GetExtendedCrashInformation()
|
|
|
|
error = crash_info.GetAsJSON(stream)
|
|
self.assertFalse(error.Success())
|
|
self.assertIn("No structured data.", error.GetCString())
|