blob: 885e73e3cec1572bc675af7fc368a9465274f643 [file] [edit]
"""
End-to-end test for accelerator plugin breakpoints.
Launches a real process against an lldb-server that has the mock accelerator
plugin enabled and verifies that the breakpoints requested by the plugin are
set in the native process, hit, and that hitting one breakpoint can request
further breakpoints. This exercises all three breakpoint types: by name, by
name scoped to a shared library, and by address.
"""
import lldb
import lldbsuite.test.lldbutil as lldbutil
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import configuration
def uint64_to_int64(value):
"""Reinterpret an unsigned 64-bit value as a signed 64-bit integer."""
if value >= (1 << 63):
return value - (1 << 64)
return value
class MockAcceleratorBreakpointsTestCase(TestBase):
NO_DEBUG_INFO_TESTCASE = True
def setUp(self):
super().setUp()
if "mock-accelerator" not in configuration.enabled_plugins:
self.skipTest("mock-accelerator plugin is not enabled")
def check_accelerator_breakpoint_stop(self, process, function_name, hit_count=None):
"""Verify the process stopped at an internal accelerator breakpoint in
the given function. If hit_count is not None, also verify the
breakpoint's hit count. Returns the breakpoint."""
self.assertState(process.GetState(), lldb.eStateStopped)
thread = process.GetSelectedThread()
# The stop must be due to a breakpoint, and the frame must be in the
# expected function.
self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonBreakpoint)
frame = thread.GetFrameAtIndex(0)
self.assertEqual(frame.GetFunctionName(), function_name)
# The breakpoint id is carried in the stop reason data. Accelerator
# breakpoints are internal, so they are not in the public breakpoint
# list, but can still be looked up by id. The datum is an unsigned
# 64-bit value holding the (signed) breakpoint id; internal ids are
# negative.
self.assertGreater(thread.GetStopReasonDataCount(), 0)
bp_id = uint64_to_int64(thread.GetStopReasonDataAtIndex(0))
bp = process.GetTarget().FindBreakpointByID(bp_id)
self.assertTrue(bp.IsValid())
self.assertTrue(bp.IsInternal(), "accelerator breakpoints are internal")
if hit_count is not None:
self.assertEqual(bp.GetHitCount(), hit_count)
return bp
@skipIfRemote
@add_test_categories(["llgs"])
def test_accelerator_breakpoints(self):
"""The mock accelerator plugin drives breakpoints in the inferior."""
self.build()
exe = self.getBuildArtifact("a.out")
target = self.dbg.CreateTarget(exe)
self.assertTrue(target, VALID_TARGET)
# Launching the process should stop at the
# "mock_gpu_accelerator_initialize" breakpoint that the mock plugin
# requested via jAcceleratorPluginInitialize (it requests the native
# process not auto-resume). This is a breakpoint by name with no shared
# library.
process = target.LaunchSimple(None, None, self.get_process_working_directory())
self.assertTrue(process, PROCESS_IS_VALID)
self.check_accelerator_breakpoint_stop(
process, "mock_gpu_accelerator_initialize", hit_count=1
)
# The accelerator breakpoint was set and hit, yet it is internal, so it
# never appears in the public breakpoint list.
self.assertEqual(target.GetNumBreakpoints(), 0)
# Hitting the mock_gpu_accelerator_initialize breakpoint caused the
# plugin to request two more breakpoints: one by address (on
# "mock_gpu_accelerator_compute", from the symbol value delivered with
# the hit) and one by name scoped to the "a.out" shared library (on
# "mock_gpu_accelerator_finish"). main() calls
# mock_gpu_accelerator_compute() first.
process.Continue()
self.check_accelerator_breakpoint_stop(
process, "mock_gpu_accelerator_compute", hit_count=1
)
process.Continue()
self.check_accelerator_breakpoint_stop(
process, "mock_gpu_accelerator_finish", hit_count=1
)
# No more accelerator breakpoints; the process runs to exit.
process.Continue()
self.assertState(process.GetState(), lldb.eStateExited)
self.assertEqual(process.GetExitStatus(), 0)