Skip to content

Commit 807fc9a

Browse files
committed
[clang] Add "debug_transparent" attribute
The `debug_transparent` 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.
1 parent 4206c37 commit 807fc9a

22 files changed

+321
-1
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,10 @@ Attribute Changes in Clang
299299

300300
- Fix a bug where clang doesn't automatically apply the ``[[gsl::Owner]]`` or
301301
``[[gsl::Pointer]]`` to STL explicit template specialization decls. (#GH109442)
302+
- Introduced a new function attribute ``__attribute__((debug_transparent))``
303+
which is intended as a hint to debuggers that they should not stop at the annotated
304+
function, but instead step through it when stepping in, and continuing directly to
305+
its caller when stepping out.
302306

303307
Improvements to Clang's diagnostics
304308
-----------------------------------

clang/include/clang/Basic/Attr.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,13 @@ def Artificial : InheritableAttr {
876876
let SimpleHandler = 1;
877877
}
878878

879+
def DebugTransparent: InheritableAttr {
880+
let Spellings = [Clang<"debug_transparent">];
881+
let Subjects = SubjectList<[Function, ObjCMethod]>;
882+
let Documentation = [DebugTransparentDocs];
883+
let SimpleHandler = 1;
884+
}
885+
879886
def XRayInstrument : InheritableAttr {
880887
let Spellings = [Clang<"xray_always_instrument">,
881888
Clang<"xray_never_instrument">];

clang/include/clang/Basic/AttrDocs.td

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7866,6 +7866,66 @@ As such, this function attribute is currently only supported on X86 targets.
78667866
}];
78677867
}
78687868

7869+
def DebugTransparentDocs : Documentation {
7870+
let Category = DocCatFunction;
7871+
let Content = [{
7872+
The ``debug_transparent`` attribute is intended as a hint for debuggers that this
7873+
function itself is not interesting, but it calls a function that might be. So, when
7874+
stepping in arrives at a function with this attribute, debuggers should transparently
7875+
step-in through it into the functions called by the annotated function (but not by
7876+
subsequent calls made by those functions), stopping at the first one its normal rules
7877+
for whether to stop says to stop at - or stepping out again if none qualify. Also, when
7878+
stepping out arrives at a function with this attribute, the debugger should continue
7879+
stepping out to its caller. This attribute is currently only supported by DWARF.
7880+
7881+
For example:
7882+
7883+
.. code-block:: c
7884+
7885+
int bar(void) {
7886+
return 42;
7887+
}
7888+
7889+
__attribute__((debug_transparent))
7890+
int foo(void) {
7891+
return bar();
7892+
}
7893+
7894+
int caller(void) {
7895+
return foo();
7896+
}
7897+
7898+
Stepping into ``foo`` should step directly into ``bar`` instead, and stepping out of ``bar``
7899+
should stop in ``caller``.
7900+
7901+
Functions with the ``debug_transparent`` attribute can be chained together:
7902+
7903+
.. code-block:: c
7904+
7905+
int baz(void) {
7906+
return 42;
7907+
}
7908+
7909+
__attribute__((debug_transparent))
7910+
int bar(void) {
7911+
return baz();
7912+
}
7913+
7914+
__attribute__((debug_transparent))
7915+
int foo(void) {
7916+
return bar();
7917+
}
7918+
7919+
int caller(void) {
7920+
return foo();
7921+
}
7922+
7923+
In this example, stepping into ``foo`` should step directly into ``baz``, and stepping out of
7924+
``baz`` should stop in ``caller``.
7925+
}];
7926+
}
7927+
7928+
78697929
def ReadOnlyPlacementDocs : Documentation {
78707930
let Category = DocCatType;
78717931
let Content = [{This attribute is attached to a structure, class or union declaration.

clang/include/clang/Basic/DiagnosticCommonKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,4 +467,6 @@ def warn_try_not_valid_on_target : Warning<
467467
"target '%0' does not support exception handling;"
468468
" 'catch' block is ignored">,
469469
InGroup<OpenMPTargetException>;
470+
def warn_debug_transparent_ignored : Warning<
471+
"'debug_transparent' attribute is ignored since it is only supported by DWARF">;
470472
}

clang/lib/CodeGen/CGDebugInfo.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "TargetInfo.h"
2222
#include "clang/AST/ASTContext.h"
2323
#include "clang/AST/Attr.h"
24+
#include "clang/AST/Attrs.inc"
2425
#include "clang/AST/DeclCXX.h"
2526
#include "clang/AST/DeclFriend.h"
2627
#include "clang/AST/DeclObjC.h"
@@ -109,6 +110,20 @@ static bool IsArtificial(VarDecl const *VD) {
109110
cast<Decl>(VD->getDeclContext())->isImplicit());
110111
}
111112

113+
static bool usesDebugTransparent(const Decl *D, const CodeGenModule &CGM) {
114+
if (!D)
115+
return false;
116+
117+
if (auto *attr = D->getAttr<DebugTransparentAttr>()) {
118+
if (CGM.getCodeGenOpts().DwarfVersion == 0)
119+
CGM.getDiags().Report(attr->getLocation(),
120+
diag::warn_debug_transparent_ignored);
121+
return true;
122+
}
123+
124+
return false;
125+
}
126+
112127
CGDebugInfo::CGDebugInfo(CodeGenModule &CGM)
113128
: CGM(CGM), DebugKind(CGM.getCodeGenOpts().getDebugInfo()),
114129
DebugTypeExtRefs(CGM.getCodeGenOpts().DebugTypeExtRefs),
@@ -4480,6 +4495,8 @@ void CGDebugInfo::emitFunctionStart(GlobalDecl GD, SourceLocation Loc,
44804495
SPFlags |= llvm::DISubprogram::SPFlagLocalToUnit;
44814496
if (CGM.getLangOpts().Optimize)
44824497
SPFlags |= llvm::DISubprogram::SPFlagOptimized;
4498+
if (usesDebugTransparent(D, CGM))
4499+
SPFlags |= llvm::DISubprogram::SPFlagIsDebugTransparent;
44834500

44844501
llvm::DINode::DIFlags FlagsForDef = Flags | getCallSiteRelatedAttrs();
44854502
llvm::DISubprogram::DISPFlags SPFlagsForDef =
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// RUN: %clang -gdwarf -emit-llvm -S %s -o - | FileCheck %s
2+
3+
void foo(void) {}
4+
5+
struct A {
6+
[[clang::debug_transparent()]]
7+
A() {
8+
foo();
9+
}
10+
11+
[[clang::debug_transparent()]]
12+
~A() {
13+
foo();
14+
}
15+
[[clang::debug_transparent()]]
16+
void method(void) {
17+
foo();
18+
}
19+
20+
[[clang::always_inline()]]
21+
[[clang::debug_transparent()]]
22+
void inline_method(void) {
23+
foo();
24+
}
25+
26+
};
27+
28+
int main() {
29+
auto a = A();
30+
a.method();
31+
a.inline_method();
32+
}
33+
34+
// CHECK: DISubprogram(name: "inline_method"{{.*}} DISPFlagIsDebugTransparent
35+
// CHECK: DISubprogram(name: "method"{{.*}} DISPFlagIsDebugTransparent
36+
// CHECK: DISubprogram(name: "A"{{.*}} DISPFlagIsDebugTransparent
37+
// CHECK: DISubprogram(name: "~A"{{.*}} DISPFlagIsDebugTransparent
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Pipe stderr to FileCheck since we're checking for a warning
2+
// RUN: %clang -gcodeview -g -emit-llvm -S %s -o - 2>&1 | FileCheck %s
3+
4+
5+
struct S {
6+
[[clang::debug_transparent]]
7+
void foo(void) {}
8+
};
9+
10+
int main() {
11+
S s;
12+
s.foo();
13+
}
14+
// CHECK: warning: 'debug_transparent' attribute is ignored since it is only supported by DWARF
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Pipe stderr to FileCheck since we're checking for a warning
2+
// RUN: %clang -gcodeview -emit-llvm -S %s -o - 2>&1 | FileCheck %s
3+
4+
5+
__attribute__((debug_transparent))
6+
void foo(void) {}
7+
8+
// Check that the warning is NOT printed when compiling without debug information.
9+
// CHECK-NOT: warning: 'debug_transparent' attribute is ignored since it is only supported by DWARF
10+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Pipe stderr to FileCheck since we're checking for a warning
2+
// RUN: %clang -gcodeview -g -emit-llvm -S %s -o - 2>&1 | FileCheck %s
3+
4+
@interface ObjCClass
5+
- (void)foo __attribute__((debug_transparent));
6+
@end
7+
8+
@implementation ObjCClass
9+
- (void)foo {}
10+
@end
11+
12+
// CHECK: warning: 'debug_transparent' attribute is ignored since it is only supported by DWARF
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: %clang -gdwarf -emit-llvm -S %s -o - | FileCheck %s
2+
3+
4+
@interface ObjCClass
5+
- (void)foo __attribute__((debug_transparent));
6+
@end
7+
8+
@implementation ObjCClass
9+
- (void)foo {}
10+
@end
11+
12+
13+
// CHECK: DISubprogram(name: "-[ObjCClass foo]"{{.*}} DISPFlagIsDebugTransparent

0 commit comments

Comments
 (0)