blob: a8a944f2d434bdac57fb1bcc361715f6fe13e8c4 [file] [log] [blame] [edit]
#!/usr/bin/env python3
"""
LLDB script to dump the contents of WebKit's IPC message log for testing.
Usage in LLDB:
(lldb) command script import Tools/Scripts/dump-message-log
(lldb) dump_message_log
The script dumps messages in reverse chronological order (most recent first),
starting from the last written position and stopping when it encounters either:
- An Invalid message name (indicating unwritten slots)
- A full traversal of the buffer (wrapping back to start)
"""
import lldb
import re
def dump_message_log(debugger, command, result, internal_dict):
"""Dumps the contents of IPC::gMessageLog in reverse chronological order"""
target = debugger.GetSelectedTarget()
if not target or not target.IsValid():
print("Error: No valid target selected", file=result)
return
process = target.GetProcess()
if not process or not process.IsValid():
print("Error: No valid process attached", file=result)
return
# Find the global message log
buffer_var = target.FindFirstGlobalVariable("IPC::gMessageLog")
if not buffer_var or not buffer_var.IsValid():
print("Error: Could not find IPC::gMessageLog", file=result)
print("Make sure the process is using a build with the message log compiled in.", file=result)
return
# Get capacity from the type name (e.g., MessageLog<256>)
buffer_type = buffer_var.GetType()
type_name = buffer_type.GetName()
match = re.search(r'MessageLog<(\d+)>', type_name)
if not match:
print(f"Error: Could not parse capacity from type name: {type_name}", file=result)
return
capacity = int(match.group(1))
# Get m_index (std::atomic<size_t>)
# Note: m_index is a private member, but LLDB can access it for debugging
m_index_var = buffer_var.GetChildMemberWithName("m_index")
if not m_index_var or not m_index_var.IsValid():
print("Error: Could not access m_index member", file=result)
return
# Extract the actual value from std::atomic
# Try different member names used by different standard library implementations
index_value = None
for atomic_member in ["_M_i", "__a_", "_Value", "Value", "__a"]:
atomic_storage = m_index_var.GetChildMemberWithName(atomic_member)
if atomic_storage and atomic_storage.IsValid():
index_value = atomic_storage.GetValueAsUnsigned()
break
if index_value is None:
# Fallback: try to get value directly
index_value = m_index_var.GetValueAsUnsigned()
if index_value == 0xFFFFFFFFFFFFFFFF: # Likely failed
print("Error: Could not read m_index value", file=result)
return
# Get m_buffer (std::array)
# Note: m_buffer is a private member, but LLDB can access it for debugging
m_buffer_var = buffer_var.GetChildMemberWithName("m_buffer")
if not m_buffer_var or not m_buffer_var.IsValid():
print("Error: Could not access m_buffer member", file=result)
return
# std::array may wrap the actual array in an __elems_ or similar member
# Try to access it, otherwise use m_buffer_var directly
elems_var = m_buffer_var.GetChildMemberWithName("__elems_")
if elems_var and elems_var.IsValid():
m_buffer_var = elems_var
# Try to find IPC::MessageName::Invalid
message_name_type = target.FindFirstType("IPC::MessageName")
message_name_invalid = None
if message_name_type and message_name_type.IsValid():
enum_members = message_name_type.GetEnumMembers()
for i in range(enum_members.GetSize()):
member = enum_members.GetTypeEnumMemberAtIndex(i)
if member.GetName() == "Invalid":
message_name_invalid = member.GetValueAsUnsigned()
break
# Print header
print("\n" + "=" * 80, file=result)
print(f"WebKit IPC Message Log Dump (Capacity: {capacity})", file=result)
print("=" * 80, file=result)
print("\nMessages (most recent first):\n", file=result)
# Calculate the last written index
# m_index points to the NEXT position to write, so last written is (m_index - 1) % capacity
if index_value == 0:
last_written = capacity - 1
else:
last_written = (index_value - 1) % capacity
current_index = last_written
messages_found = 0
for i in range(capacity):
# Get array element at current_index
element = m_buffer_var.GetChildAtIndex(current_index)
if not element or not element.IsValid():
print(f"Warning: Could not read buffer[{current_index}]", file=result)
break
message_value = element.GetValueAsUnsigned()
# Stop if we encounter Invalid
if message_name_invalid is not None and message_value == message_name_invalid:
print(f"\n[Stopped at Invalid message after {messages_found} messages (at buffer[{current_index}])]", file=result)
break
# Try to get the symbolic name
message_name_str = element.GetValue()
if message_name_str:
# Clean up the format (e.g., "IPCTester_EmptyMessage" instead of full enum path)
message_name_str = message_name_str.replace("IPC::MessageName::", "")
# Display "Invalid" instead of "Count" since they're semantically invalid messages
if message_name_str == "Count":
message_name_str = "Invalid"
else:
message_name_str = f"<value={message_value}>"
print(f"{messages_found}. {message_name_str}", file=result)
messages_found += 1
# Move to previous index (wrap around)
if current_index == 0:
current_index = capacity - 1
else:
current_index -= 1
print("\n" + "=" * 80 + "\n", file=result)
def __lldb_init_module(debugger, internal_dict):
"""Register the command when the script is loaded"""
debugger.HandleCommand(
'command script add -f dump_message_log.dump_message_log dump_message_log'
)
print('The "dump_message_log" command has been installed.')
if __name__ == "__main__":
print("This script is intended to be used within LLDB, not run directly.")
print("\nUsage in LLDB:")
print(" (lldb) command script import Tools/Scripts/dump-message-log")
print(" (lldb) dump_message_log")