Previously lldb didn't support setting breakpoints on call site locations. This patch adds that ability. It would be very slow if we did this by searching all the debug information for every inlined subroutine record looking for a call-site match, so I added one restriction to the call-site support. This change will find all call sites for functions that also supply at least one line to the regular line table. That way we can use the fact that the line table search will move the location to that subsequent line (but only within the same function). When we find an actually moved source line match, we can search in the function that contained that line table entry for the call-site, and set the breakpoint location back to that. When I started writing tests for this new ability, it quickly became obvious that our support for virtual inline stepping was pretty buggy. We didn't print the right file & line number for the breakpoint, and we didn't set the position in the "virtual inlined stack" correctly when we hit the breakpoint. We also didn't step through the inlined frames correctly. There was code to try to detect the right inlined stack position, but it had been refactored a while back with the comment that it was super confusing and the refactor was supposed to make it clearer, but the refactor didn't work either. That code was made much clearer by abstracting the job of "handling the stack readjustment" to the various StopInfo's. Previously, there was a big (and buggy) switch over stop info's. Moving the responsibility to the stop info made this code much easier to reason about. We also had no tests for virtual inlined stepping (our inlined stepping test was actually written specifically to avoid the formation of a virtual inlined stack... So I also added tests for that along with the tests for setting the call-site breakpoints.
162 lines
3.9 KiB
C++
162 lines
3.9 KiB
C++
#include <algorithm>
|
|
#include <cstdio>
|
|
#include <string>
|
|
|
|
inline int inline_ref_1 (int &value) __attribute__((always_inline));
|
|
inline int inline_ref_2 (int &value) __attribute__((always_inline));
|
|
|
|
int caller_ref_1 (int &value);
|
|
int caller_ref_2 (int &value);
|
|
|
|
int called_by_inline_ref (int &value);
|
|
|
|
inline void inline_trivial_1 () __attribute__((always_inline));
|
|
inline void inline_trivial_2 () __attribute__((always_inline));
|
|
|
|
// These three should share the same initial pc so we can test
|
|
// virtual inline stepping.
|
|
inline void caller_trivial_inline_1() __attribute__((always_inline));
|
|
inline void caller_trivial_inline_2() __attribute__((always_inline));
|
|
inline void caller_trivial_inline_3() __attribute__((always_inline));
|
|
|
|
void caller_trivial_1 ();
|
|
void caller_trivial_2 ();
|
|
|
|
void called_by_inline_trivial ();
|
|
|
|
static int inline_value;
|
|
|
|
int
|
|
function_to_call ()
|
|
{
|
|
return inline_value;
|
|
}
|
|
|
|
int
|
|
caller_ref_1 (int &value)
|
|
{
|
|
int increment = caller_ref_2(value); // In caller_ref_1.
|
|
value += increment; // At increment in caller_ref_1.
|
|
return value;
|
|
}
|
|
|
|
int
|
|
caller_ref_2 (int &value)
|
|
{
|
|
int increment = inline_ref_1 (value); // In caller_ref_2.
|
|
value += increment; // At increment in caller_ref_2.
|
|
return value;
|
|
}
|
|
|
|
int
|
|
called_by_inline_ref (int &value)
|
|
{
|
|
value += 1; // In called_by_inline_ref.
|
|
return value;
|
|
}
|
|
|
|
int
|
|
inline_ref_1 (int &value)
|
|
{
|
|
int increment = inline_ref_2(value); // In inline_ref_1.
|
|
value += increment; // At increment in inline_ref_1.
|
|
return value;
|
|
}
|
|
|
|
int
|
|
inline_ref_2 (int &value)
|
|
{
|
|
int increment = called_by_inline_ref (value); // In inline_ref_2.
|
|
value += 1; // At increment in inline_ref_2.
|
|
return value;
|
|
}
|
|
|
|
void
|
|
caller_trivial_1 ()
|
|
{
|
|
caller_trivial_2(); // In caller_trivial_1.
|
|
inline_value += 1;
|
|
}
|
|
|
|
void
|
|
caller_trivial_2 ()
|
|
{
|
|
asm volatile ("nop"); inline_trivial_1 (); // In caller_trivial_2.
|
|
inline_value += 1; // At increment in caller_trivial_2.
|
|
}
|
|
|
|
// When you call caller_trivial_inline_1, the inlined call-site
|
|
// should share a PC with all three of the following inlined
|
|
// functions, so we can exercise "virtual inline stepping".
|
|
void caller_trivial_inline_1() {
|
|
caller_trivial_inline_2(); // In caller_trivial_inline_1.
|
|
inline_value += 1;
|
|
}
|
|
|
|
void caller_trivial_inline_2() {
|
|
caller_trivial_inline_3(); // In caller_trivial_inline_2.
|
|
inline_value += 1;
|
|
}
|
|
|
|
void caller_trivial_inline_3() {
|
|
inline_value += 1; // In caller_trivial_inline_3.
|
|
}
|
|
|
|
void
|
|
called_by_inline_trivial ()
|
|
{
|
|
inline_value += 1; // In called_by_inline_trivial.
|
|
}
|
|
|
|
void
|
|
inline_trivial_1 ()
|
|
{
|
|
asm volatile ("nop"); inline_trivial_2(); // In inline_trivial_1.
|
|
inline_value += 1; // At increment in inline_trivial_1.
|
|
}
|
|
|
|
void
|
|
inline_trivial_2 ()
|
|
{
|
|
inline_value += 1; // In inline_trivial_2.
|
|
called_by_inline_trivial (); // At caller_by_inline_trivial in inline_trivial_2.
|
|
}
|
|
|
|
template<typename T> T
|
|
max_value(const T& lhs, const T& rhs)
|
|
{
|
|
return std::max(lhs, rhs); // In max_value template
|
|
}
|
|
|
|
template<> std::string
|
|
max_value(const std::string& lhs, const std::string& rhs)
|
|
{
|
|
return (lhs.size() > rhs.size()) ? lhs : rhs; // In max_value specialized
|
|
}
|
|
|
|
int
|
|
main (int argc, char **argv)
|
|
{
|
|
|
|
inline_value = 0; // Stop here and step over to set up stepping over.
|
|
|
|
inline_trivial_1 (); // At inline_trivial_1 called from main.
|
|
|
|
caller_trivial_1(); // At first call of caller_trivial_1 in main.
|
|
|
|
caller_trivial_1(); // At second call of caller_trivial_1 in main.
|
|
|
|
caller_ref_1 (argc); // At first call of caller_ref_1 in main.
|
|
|
|
caller_ref_1 (argc); // At second call of caller_ref_1 in main.
|
|
|
|
function_to_call (); // Make sure debug info for this function gets generated.
|
|
|
|
max_value(123, 456); // Call max_value template
|
|
max_value(std::string("abc"), std::string("0022")); // Call max_value specialized
|
|
|
|
caller_trivial_inline_1(); // At caller_trivial_inline_1.
|
|
|
|
return 0; // About to return from main.
|
|
}
|