Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,12 @@ def Artificial : InheritableAttr {
let SimpleHandler = 1;
}

def TransparentStepping: InheritableAttr {
let Spellings = [Clang<"transparent_stepping">];
let Subjects = SubjectList<[Function]>;
let Documentation = [TransparentSteppingDocs];
}

def XRayInstrument : InheritableAttr {
let Spellings = [Clang<"xray_always_instrument">,
Clang<"xray_never_instrument">];
Expand Down
60 changes: 60 additions & 0 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -6924,6 +6924,66 @@ As such, this function attribute is currently only supported on X86 targets.
}];
}

def TransparentSteppingDocs : Documentation {
let Category = DocCatFunction;
let Content = [{
The ``transparent_stepping`` attribute is intended as a hint for debuggers that this
function itself is not interesting, but it calls a function that might be. So, when
stepping in arrives at a function with this attribute, debuggers should transparently
step-in through it into the functions called by the annotated function (but not by
subsequent calls made by those functions), stopping at the first one its normal rules
for whether to stop says to stop at - or stepping out again if none qualify. Also, when
stepping out arrives at a function with this attribute, the debugger should continue
stepping out to its caller.

For example:

.. code-block:: c

int bar(void) {
return 42;
}

__attribute__((transparent_stepping))
int foo(void) {
return bar();
}

int caller(void) {
return foo();
}

Stepping into ``foo`` should step directly into ``bar`` instead, and stepping out of ``bar``
should stop in ``caller``.

Functions with the ``transparent_stepping`` attribute can be chained together:

.. code-block:: c

int baz(void) {
return 42;
}

__attribute__((transparent_stepping))
int bar(void) {
return baz();
}

__attribute__((transparent_stepping))
int foo(void) {
return bar();
}

int caller(void) {
return foo();
}

In this example, stepping into ``foo`` should step directly into ``baz``, and stepping out of
``baz`` should stop in ``caller``.
}];
}


def ReadOnlyPlacementDocs : Documentation {
let Category = DocCatType;
let Content = [{This attribute is attached to a structure, class or union declaration.
Expand Down
17 changes: 17 additions & 0 deletions clang/lib/CodeGen/CGDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ static uint32_t getDeclAlignIfRequired(const Decl *D, const ASTContext &Ctx) {
return D->hasAttr<AlignedAttr>() ? D->getMaxAlignment() : 0;
}

static bool getIsTransparentStepping(const Decl *D) {
if (!D)
return false;
return D->hasAttr<TransparentSteppingAttr>();
}

CGDebugInfo::CGDebugInfo(CodeGenModule &CGM)
: CGM(CGM), DebugKind(CGM.getCodeGenOpts().getDebugInfo()),
DebugTypeExtRefs(CGM.getCodeGenOpts().DebugTypeExtRefs),
Expand Down Expand Up @@ -1882,6 +1888,8 @@ llvm::DISubprogram *CGDebugInfo::CreateCXXMemberFunction(
SPFlags |= llvm::DISubprogram::SPFlagLocalToUnit;
if (CGM.getLangOpts().Optimize)
SPFlags |= llvm::DISubprogram::SPFlagOptimized;
if (getIsTransparentStepping(Method))
SPFlags |= llvm::DISubprogram::SPFlagIsTransparentStepping;

// In this debug mode, emit type info for a class when its constructor type
// info is emitted.
Expand Down Expand Up @@ -3809,6 +3817,8 @@ llvm::DISubprogram *CGDebugInfo::getFunctionFwdDeclOrStub(GlobalDecl GD,
if (Stub) {
Flags |= getCallSiteRelatedAttrs();
SPFlags |= llvm::DISubprogram::SPFlagDefinition;
if (getIsTransparentStepping(FD))
SPFlags |= llvm::DISubprogram::SPFlagIsTransparentStepping;
return DBuilder.createFunction(
DContext, Name, LinkageName, Unit, Line,
getOrCreateFunctionType(GD.getDecl(), FnType, Unit), 0, Flags, SPFlags,
Expand Down Expand Up @@ -3958,6 +3968,8 @@ llvm::DISubprogram *CGDebugInfo::getObjCMethodDeclaration(
if (It == TypeCache.end())
return nullptr;
auto *InterfaceType = cast<llvm::DICompositeType>(It->second);
if (getIsTransparentStepping(D))
SPFlags |= llvm::DISubprogram::SPFlagIsTransparentStepping;
llvm::DISubprogram *FD = DBuilder.createFunction(
InterfaceType, getObjCMethodName(OMD), StringRef(),
InterfaceType->getFile(), LineNo, FnType, LineNo, Flags, SPFlags);
Expand Down Expand Up @@ -4124,6 +4136,8 @@ void CGDebugInfo::emitFunctionStart(GlobalDecl GD, SourceLocation Loc,
SPFlags |= llvm::DISubprogram::SPFlagLocalToUnit;
if (CGM.getLangOpts().Optimize)
SPFlags |= llvm::DISubprogram::SPFlagOptimized;
if (getIsTransparentStepping(D))
SPFlags |= llvm::DISubprogram::SPFlagIsTransparentStepping;

llvm::DINode::DIFlags FlagsForDef = Flags | getCallSiteRelatedAttrs();
llvm::DISubprogram::DISPFlags SPFlagsForDef =
Expand Down Expand Up @@ -4210,6 +4224,9 @@ void CGDebugInfo::EmitFunctionDecl(GlobalDecl GD, SourceLocation Loc,

llvm::DINodeArray Annotations = CollectBTFDeclTagAnnotations(D);
llvm::DISubroutineType *STy = getOrCreateFunctionType(D, FnType, Unit);
if (getIsTransparentStepping(D))
SPFlags |= llvm::DISubprogram::SPFlagIsTransparentStepping;

llvm::DISubprogram *SP = DBuilder.createFunction(
FDContext, Name, LinkageName, Unit, LineNo, STy, ScopeLine, Flags,
SPFlags, TParamsArray.get(), nullptr, nullptr, Annotations);
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6687,6 +6687,12 @@ static void handleSwiftAsyncName(Sema &S, Decl *D, const ParsedAttr &AL) {
D->addAttr(::new (S.Context) SwiftAsyncNameAttr(S.Context, AL, Name));
}

static void handleTransparentStepping(Sema &S, Decl *D,
const ParsedAttr &AL) {
D->addAttr(::new (S.Context)
TransparentSteppingAttr(S.Context, AL));
}

static void handleSwiftNewType(Sema &S, Decl *D, const ParsedAttr &AL) {
// Make sure that there is an identifier as the annotation's single argument.
if (!AL.checkExactlyNumArgs(S, 1))
Expand Down Expand Up @@ -8948,6 +8954,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_NoDebug:
handleNoDebugAttr(S, D, AL);
break;
case ParsedAttr::AT_TransparentStepping:
handleTransparentStepping(S, D, AL);
break;
case ParsedAttr::AT_CmseNSEntry:
handleCmseNSEntryAttr(S, D, AL);
break;
Expand Down
16 changes: 16 additions & 0 deletions clang/test/CodeGen/attr-transparent-stepping-method.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// RUN: %clang_cc1 -debug-info-kind=limited -emit-llvm -o - %s | FileCheck %s

void bar(void) {}

struct A {
[[clang::transparent_stepping()]]
void foo(void) {
bar();
}
};

int main() {
A().foo();
}

// CHECK: DISubprogram(name: "foo"{{.*}} DISPFlagIsTransparentStepping
10 changes: 10 additions & 0 deletions clang/test/CodeGen/attr-transparent-stepping.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// RUN: %clang_cc1 -debug-info-kind=limited -emit-llvm -o - %s | FileCheck %s

void bar(void) {}

__attribute__((transparent_stepping))
void foo(void) {
bar();
}

// CHECK: DISubprogram(name: "foo"{{.*}} DISPFlagIsTransparentStepping
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@
// CHECK-NEXT: Target (SubjectMatchRule_function)
// CHECK-NEXT: TargetClones (SubjectMatchRule_function)
// CHECK-NEXT: TestTypestate (SubjectMatchRule_function_is_member)
// CHECK-NEXT: TransparentStepping (SubjectMatchRule_function)
// CHECK-NEXT: TrivialABI (SubjectMatchRule_record)
// CHECK-NEXT: Uninitialized (SubjectMatchRule_variable_is_local)
// CHECK-NEXT: UseHandle (SubjectMatchRule_variable_is_parameter)
Expand Down
7 changes: 7 additions & 0 deletions clang/test/Sema/attr-transparent-stepping.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// RUN: %clang_cc1 %s -verify -fsyntax-only

__attribute__((transparent_stepping))
void correct(void) {}

__attribute__((transparent_stepping(1))) // expected-error {{'transparent_stepping' attribute takes no arguments}}
void wrong_arg(void) {}
11 changes: 11 additions & 0 deletions clang/test/SemaCXX/attr-transparent-stepping-method.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// RUN: %clang_cc1 %s -verify -fsyntax-only


struct S {
[[clang::transparent_stepping]]
void correct(void) {}

[[clang::transparent_stepping(1)]] // expected-error {{'transparent_stepping' attribute takes no arguments}}
void one_arg(void) {}
};

8 changes: 7 additions & 1 deletion lldb/include/lldb/Symbol/Function.h
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ class Function : public UserID, public SymbolContextScope {
Function(CompileUnit *comp_unit, lldb::user_id_t func_uid,
lldb::user_id_t func_type_uid, const Mangled &mangled,
Type *func_type, const AddressRange &range,
bool can_throw = false);
bool can_throw = false, bool generic_trampoline = false);

/// Destructor.
~Function() override;
Expand Down Expand Up @@ -554,6 +554,10 @@ class Function : public UserID, public SymbolContextScope {
/// A type object pointer.
Type *GetType();

bool IsGenericTrampoline() const {
return m_is_generic_trampoline;
}

/// Get const accessor for the type that describes the function return value
/// type, and parameter types.
///
Expand Down Expand Up @@ -659,6 +663,8 @@ class Function : public UserID, public SymbolContextScope {
/// information.
Mangled m_mangled;

bool m_is_generic_trampoline;

/// All lexical blocks contained in this function.
Block m_block;

Expand Down
6 changes: 6 additions & 0 deletions lldb/include/lldb/Target/Target.h
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,12 @@ class TargetProperties : public Properties {

bool GetDebugUtilityExpression() const;

/// Trampoline support includes stepping through trampolines directly to their
/// targets, stepping out of trampolines directly to their callers, and
/// automatically filtering out trampolines as possible breakpoint locations
/// when set by name.
bool GetEnableTrampolineSupport() const;

private:
// Callbacks for m_launch_info.
void Arg0ValueChangedCallback();
Expand Down
21 changes: 21 additions & 0 deletions lldb/include/lldb/Target/Thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -904,6 +904,27 @@ class Thread : public std::enable_shared_from_this<Thread>,
bool abort_other_plans, bool stop_other_threads,
Status &status);

/// Gets the plan used to step through a function with a generic trampoline. A
/// generic trampoline is one without a function target, which the thread plan
/// will attempt to step through until it finds a place where it makes sense
/// to stop at.
/// \param[in] abort_other_plans
/// \b true if we discard the currently queued plans and replace them with
/// this one.
/// Otherwise this plan will go on the end of the plan stack.
///
/// \param[in] stop_other_threads
/// \b true if we will stop other threads while we single step this one.
///
/// \param[out] status
/// A status with an error if queuing failed.
///
/// \return
/// A shared pointer to the newly queued thread plan, or nullptr if the
/// plan could not be queued.
virtual lldb::ThreadPlanSP QueueThreadPlanForStepThroughGenericTrampoline(
bool abort_other_plans, lldb::RunMode stop_other_threads, Status &status);

/// Gets the plan used to continue from the current PC.
/// This is a simple plan, mostly useful as a backstop when you are continuing
/// for some particular purpose.
Expand Down
1 change: 1 addition & 0 deletions lldb/include/lldb/Target/ThreadPlan.h
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ class ThreadPlan : public std::enable_shared_from_this<ThreadPlan>,
eKindStepInRange,
eKindRunToAddress,
eKindStepThrough,
eKindStepThroughGenericTrampoline,
eKindStepUntil
};

Expand Down
1 change: 0 additions & 1 deletion lldb/include/lldb/Target/ThreadPlanStepOverRange.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ class ThreadPlanStepOverRange : public ThreadPlanStepRange,
bool ShouldStop(Event *event_ptr) override;

protected:
bool DoPlanExplainsStop(Event *event_ptr) override;
bool DoWillResume(lldb::StateType resume_state, bool current_plan) override;

void SetFlagsToDefault() override {
Expand Down
1 change: 1 addition & 0 deletions lldb/include/lldb/Target/ThreadPlanStepRange.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class ThreadPlanStepRange : public ThreadPlan {
void AddRange(const AddressRange &new_range);

protected:
bool DoPlanExplainsStop(Event *event_ptr) override;
bool InRange();
lldb::FrameComparison CompareCurrentFrameToStartFrame();
bool InSymbol();
Expand Down
52 changes: 52 additions & 0 deletions lldb/include/lldb/Target/ThreadPlanStepThroughGenericTrampoline.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//===-- ThreadPlanStepInRange.h ---------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLDB_TARGET_THREADPLANSTEPTHROUGHGENERICTRAMPOLINE_H
#define LLDB_TARGET_THREADPLANSTEPTHROUGHGENERICTRAMPOLINE_H

#include "lldb/Target/Thread.h"
#include "lldb/Target/ThreadPlanShouldStopHere.h"
#include "lldb/Target/ThreadPlanStepRange.h"

namespace lldb_private {

class ThreadPlanStepThroughGenericTrampoline : public ThreadPlanStepRange,
public ThreadPlanShouldStopHere {
public:
ThreadPlanStepThroughGenericTrampoline(Thread &thread,
lldb::RunMode stop_others);

~ThreadPlanStepThroughGenericTrampoline() override;

void GetDescription(Stream *s, lldb::DescriptionLevel level) override;

bool ShouldStop(Event *event_ptr) override;
bool ValidatePlan(Stream *error) override;

protected:
void SetFlagsToDefault() override {
GetFlags().Set(
ThreadPlanStepThroughGenericTrampoline::s_default_flag_values);
}

private:
// Need an appropriate marker for the current stack so we can tell step out
// from step in.

static uint32_t
s_default_flag_values; // These are the default flag values
// for the ThreadPlanStepThroughGenericTrampoline.
ThreadPlanStepThroughGenericTrampoline(
const ThreadPlanStepThroughGenericTrampoline &) = delete;
const ThreadPlanStepThroughGenericTrampoline &
operator=(const ThreadPlanStepThroughGenericTrampoline &) = delete;
};

} // namespace lldb_private

#endif // LLDB_TARGET_THREADPLANSTEPTHROUGHGENERICTRAMPOLINE_H
5 changes: 5 additions & 0 deletions lldb/source/Core/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -799,7 +799,12 @@ void Module::LookupInfo::Prune(SymbolContextList &sc_list,
if (!sc_list.GetContextAtIndex(i, sc))
break;

bool is_trampoline =
Target::GetGlobalProperties().GetEnableTrampolineSupport() &&
sc.function && sc.function->IsGenericTrampoline();

bool keep_it =
!is_trampoline &&
NameMatchesLookupInfo(sc.GetFunctionName(), sc.GetLanguage());
if (keep_it)
++i;
Expand Down
6 changes: 5 additions & 1 deletion lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2395,12 +2395,16 @@ DWARFASTParserClang::ParseFunctionFromDWARF(CompileUnit &comp_unit,

assert(func_type == nullptr || func_type != DIE_IS_BEING_PARSED);

bool is_generic_trampoline = die.IsGenericTrampoline();

const user_id_t func_user_id = die.GetID();
func_sp =
std::make_shared<Function>(&comp_unit,
func_user_id, // UserID is the DIE offset
func_user_id, func_name, func_type,
func_range); // first address range
func_range, // first address range
false, // canThrow
is_generic_trampoline);

if (func_sp.get() != nullptr) {
if (frame_base.IsValid())
Expand Down
Loading