260 lines
11 KiB
Python
260 lines
11 KiB
Python
|
"""
|
||
|
Make sure the frame variable -g, -a, and -l flags work.
|
||
|
"""
|
||
|
|
||
|
|
||
|
import lldb
|
||
|
import lldbsuite.test.lldbutil as lldbutil
|
||
|
from lldbsuite.test.decorators import *
|
||
|
from lldbsuite.test.lldbtest import *
|
||
|
import os
|
||
|
import shutil
|
||
|
import time
|
||
|
|
||
|
|
||
|
class TestFrameVar(TestBase):
|
||
|
# If your test case doesn't stress debug info, then
|
||
|
# set this to true. That way it won't be run once for
|
||
|
# each debug info format.
|
||
|
NO_DEBUG_INFO_TESTCASE = True
|
||
|
|
||
|
def test_frame_var(self):
|
||
|
self.build()
|
||
|
self.do_test()
|
||
|
|
||
|
def do_test(self):
|
||
|
target = self.createTestTarget()
|
||
|
|
||
|
# Now create a breakpoint in main.c at the source matching
|
||
|
# "Set a breakpoint here"
|
||
|
breakpoint = target.BreakpointCreateBySourceRegex(
|
||
|
"Set a breakpoint here", lldb.SBFileSpec("main.c")
|
||
|
)
|
||
|
self.assertTrue(
|
||
|
breakpoint and breakpoint.GetNumLocations() >= 1, VALID_BREAKPOINT
|
||
|
)
|
||
|
|
||
|
error = lldb.SBError()
|
||
|
# This is the launch info. If you want to launch with arguments or
|
||
|
# environment variables, add them using SetArguments or
|
||
|
# SetEnvironmentEntries
|
||
|
|
||
|
launch_info = target.GetLaunchInfo()
|
||
|
process = target.Launch(launch_info, error)
|
||
|
self.assertTrue(process, PROCESS_IS_VALID)
|
||
|
|
||
|
# Did we hit our breakpoint?
|
||
|
from lldbsuite.test.lldbutil import get_threads_stopped_at_breakpoint
|
||
|
|
||
|
threads = get_threads_stopped_at_breakpoint(process, breakpoint)
|
||
|
self.assertEqual(
|
||
|
len(threads), 1, "There should be a thread stopped at our breakpoint"
|
||
|
)
|
||
|
|
||
|
# The hit count for the breakpoint should be 1.
|
||
|
self.assertEquals(breakpoint.GetHitCount(), 1)
|
||
|
|
||
|
frame = threads[0].GetFrameAtIndex(0)
|
||
|
command_result = lldb.SBCommandReturnObject()
|
||
|
interp = self.dbg.GetCommandInterpreter()
|
||
|
|
||
|
# Ensure --regex can find globals if it is the very first frame var command.
|
||
|
self.expect("frame var --regex g_", substrs=["g_var"])
|
||
|
|
||
|
# Ensure the requested scope is respected:
|
||
|
self.expect(
|
||
|
"frame var --regex argc --no-args",
|
||
|
error=True,
|
||
|
substrs=["no variables matched the regular expression 'argc'"],
|
||
|
)
|
||
|
|
||
|
# Just get args:
|
||
|
result = interp.HandleCommand("frame var -l", command_result)
|
||
|
self.assertEqual(
|
||
|
result, lldb.eReturnStatusSuccessFinishResult, "frame var -a didn't succeed"
|
||
|
)
|
||
|
output = command_result.GetOutput()
|
||
|
self.assertIn("argc", output, "Args didn't find argc")
|
||
|
self.assertIn("argv", output, "Args didn't find argv")
|
||
|
self.assertNotIn("test_var", output, "Args found a local")
|
||
|
self.assertNotIn("g_var", output, "Args found a global")
|
||
|
|
||
|
# Just get locals:
|
||
|
result = interp.HandleCommand("frame var -a", command_result)
|
||
|
self.assertEqual(
|
||
|
result, lldb.eReturnStatusSuccessFinishResult, "frame var -a didn't succeed"
|
||
|
)
|
||
|
output = command_result.GetOutput()
|
||
|
self.assertNotIn("argc", output, "Locals found argc")
|
||
|
self.assertNotIn("argv", output, "Locals found argv")
|
||
|
self.assertIn("test_var", output, "Locals didn't find test_var")
|
||
|
self.assertNotIn("g_var", output, "Locals found a global")
|
||
|
|
||
|
# Get the file statics:
|
||
|
result = interp.HandleCommand("frame var -l -a -g", command_result)
|
||
|
self.assertEqual(
|
||
|
result, lldb.eReturnStatusSuccessFinishResult, "frame var -a didn't succeed"
|
||
|
)
|
||
|
output = command_result.GetOutput()
|
||
|
self.assertNotIn("argc", output, "Globals found argc")
|
||
|
self.assertNotIn("argv", output, "Globals found argv")
|
||
|
self.assertNotIn("test_var", output, "Globals found test_var")
|
||
|
self.assertIn("g_var", output, "Globals didn't find g_var")
|
||
|
|
||
|
def check_frame_variable_errors(self, thread, error_strings):
|
||
|
command_result = lldb.SBCommandReturnObject()
|
||
|
interp = self.dbg.GetCommandInterpreter()
|
||
|
result = interp.HandleCommand("frame variable", command_result)
|
||
|
self.assertEqual(
|
||
|
result, lldb.eReturnStatusFailed, "frame var succeeded unexpectedly"
|
||
|
)
|
||
|
command_error = command_result.GetError()
|
||
|
|
||
|
frame = thread.GetFrameAtIndex(0)
|
||
|
var_list = frame.GetVariables(True, True, False, True)
|
||
|
self.assertEqual(var_list.GetSize(), 0)
|
||
|
api_error = var_list.GetError().GetCString()
|
||
|
|
||
|
for s in error_strings:
|
||
|
self.assertIn(s, command_error)
|
||
|
for s in error_strings:
|
||
|
self.assertIn(s, api_error)
|
||
|
|
||
|
@skipIfRemote
|
||
|
@skipUnlessDarwin
|
||
|
def test_darwin_dwarf_missing_obj(self):
|
||
|
"""
|
||
|
Test that if we build a binary with DWARF in .o files and we remove
|
||
|
the .o file for main.cpp, that we get an appropriate error when we
|
||
|
do 'frame variable' that explains why we aren't seeing variables.
|
||
|
"""
|
||
|
self.build(debug_info="dwarf")
|
||
|
exe = self.getBuildArtifact("a.out")
|
||
|
main_obj = self.getBuildArtifact("main.o")
|
||
|
# Delete the main.o file that contains the debug info so we force an
|
||
|
# error when we run to main and try to get variables
|
||
|
os.unlink(main_obj)
|
||
|
|
||
|
# We have to set a named breakpoint because we don't have any debug info
|
||
|
# because we deleted the main.o file.
|
||
|
(target, process, thread, bkpt) = lldbutil.run_to_name_breakpoint(self, "main")
|
||
|
error_strings = [
|
||
|
'debug map object file "',
|
||
|
'main.o" containing debug info does not exist, debug info will not be loaded',
|
||
|
]
|
||
|
self.check_frame_variable_errors(thread, error_strings)
|
||
|
|
||
|
@skipIfRemote
|
||
|
@skipUnlessDarwin
|
||
|
def test_darwin_dwarf_obj_mod_time_mismatch(self):
|
||
|
"""
|
||
|
Test that if we build a binary with DWARF in .o files and we update
|
||
|
the mod time of the .o file for main.cpp, that we get an appropriate
|
||
|
error when we do 'frame variable' that explains why we aren't seeing
|
||
|
variables.
|
||
|
"""
|
||
|
self.build(debug_info="dwarf")
|
||
|
exe = self.getBuildArtifact("a.out")
|
||
|
main_obj = self.getBuildArtifact("main.o")
|
||
|
|
||
|
# Set the modification time for main.o file to the current time after
|
||
|
# sleeping for 2 seconds. This ensures the modification time will have
|
||
|
# changed and will not match the modification time in the debug map and
|
||
|
# force an error when we run to main and try to get variables
|
||
|
time.sleep(2)
|
||
|
os.utime(main_obj, None)
|
||
|
|
||
|
# We have to set a named breakpoint because we don't have any debug info
|
||
|
# because we deleted the main.o file since the mod times don't match
|
||
|
# and debug info won't be loaded
|
||
|
(target, process, thread, bkpt) = lldbutil.run_to_name_breakpoint(self, "main")
|
||
|
|
||
|
error_strings = [
|
||
|
'debug map object file "',
|
||
|
'main.o" changed (actual: 0x',
|
||
|
", debug map: 0x",
|
||
|
") since this executable was linked, debug info will not be loaded",
|
||
|
]
|
||
|
self.check_frame_variable_errors(thread, error_strings)
|
||
|
|
||
|
@skipIfRemote
|
||
|
@skipIfWindows # Windows can't set breakpoints by name 'main' in this case.
|
||
|
def test_gline_tables_only(self):
|
||
|
"""
|
||
|
Test that if we build a binary with "-gline-tables-only" that we can
|
||
|
set a file and line breakpoint successfully, and get an error
|
||
|
letting us know that this build option was enabled when trying to
|
||
|
read variables.
|
||
|
"""
|
||
|
self.build(dictionary={"CFLAGS_EXTRAS": "-gline-tables-only"})
|
||
|
exe = self.getBuildArtifact("a.out")
|
||
|
|
||
|
# We have to set a named breakpoint because we don't have any debug info
|
||
|
# because we deleted the main.o file since the mod times don't match
|
||
|
# and debug info won't be loaded
|
||
|
(target, process, thread, bkpt) = lldbutil.run_to_name_breakpoint(self, "main")
|
||
|
error_strings = [
|
||
|
"no variable information is available in debug info for this compile unit"
|
||
|
]
|
||
|
self.check_frame_variable_errors(thread, error_strings)
|
||
|
|
||
|
@skipUnlessPlatform(["linux", "freebsd"])
|
||
|
@add_test_categories(["dwo"])
|
||
|
def test_fission_missing_dwo(self):
|
||
|
"""
|
||
|
Test that if we build a binary with "-gsplit-dwarf" that we can
|
||
|
set a file and line breakpoint successfully, and get an error
|
||
|
letting us know we were unable to load the .dwo file.
|
||
|
"""
|
||
|
self.build(dictionary={"CFLAGS_EXTRAS": "-gsplit-dwarf"})
|
||
|
exe = self.getBuildArtifact("a.out")
|
||
|
main_dwo = self.getBuildArtifact("main.dwo")
|
||
|
|
||
|
self.assertTrue(
|
||
|
os.path.exists(main_dwo), 'Make sure "%s" file exists' % (main_dwo)
|
||
|
)
|
||
|
# Delete the main.dwo file that contains the debug info so we force an
|
||
|
# error when we run to main and try to get variables.
|
||
|
os.unlink(main_dwo)
|
||
|
|
||
|
# We have to set a named breakpoint because we don't have any debug info
|
||
|
# because we deleted the main.o file since the mod times don't match
|
||
|
# and debug info won't be loaded
|
||
|
(target, process, thread, bkpt) = lldbutil.run_to_name_breakpoint(self, "main")
|
||
|
error_strings = [
|
||
|
'unable to locate .dwo debug file "',
|
||
|
'main.dwo" for skeleton DIE 0x',
|
||
|
]
|
||
|
self.check_frame_variable_errors(thread, error_strings)
|
||
|
|
||
|
@skipUnlessPlatform(["linux", "freebsd"])
|
||
|
@add_test_categories(["dwo"])
|
||
|
def test_fission_invalid_dwo_objectfile(self):
|
||
|
"""
|
||
|
Test that if we build a binary with "-gsplit-dwarf" that we can
|
||
|
set a file and line breakpoint successfully, and get an error
|
||
|
letting us know we were unable to load the .dwo file because it
|
||
|
existed, but it wasn't a valid object file.
|
||
|
"""
|
||
|
self.build(dictionary={"CFLAGS_EXTRAS": "-gsplit-dwarf"})
|
||
|
exe = self.getBuildArtifact("a.out")
|
||
|
main_dwo = self.getBuildArtifact("main.dwo")
|
||
|
|
||
|
self.assertTrue(
|
||
|
os.path.exists(main_dwo), 'Make sure "%s" file exists' % (main_dwo)
|
||
|
)
|
||
|
# Overwrite the main.dwo with the main.c source file so that the .dwo
|
||
|
# file exists, but it isn't a valid object file as there is an error
|
||
|
# for this case.
|
||
|
shutil.copyfile(self.getSourcePath("main.c"), main_dwo)
|
||
|
|
||
|
# We have to set a named breakpoint because we don't have any debug info
|
||
|
# because we deleted the main.o file since the mod times don't match
|
||
|
# and debug info won't be loaded
|
||
|
(target, process, thread, bkpt) = lldbutil.run_to_name_breakpoint(self, "main")
|
||
|
error_strings = [
|
||
|
'unable to load object file for .dwo debug file "'
|
||
|
'main.dwo" for unit DIE 0x',
|
||
|
]
|
||
|
self.check_frame_variable_errors(thread, error_strings)
|