Skip to content

Commit a1609a3

Browse files
jbachorikclaude
andcommitted
Detect per-library aarch64 frame layout from DWARF
Wire up default_clang_frame which was defined but never used. DwarfParser now detects the linked frame size by observing the first FP-based CFA entry with a non-zero offset during DWARF parsing (both GCC and Clang emit cfa_off=LINKED_FRAME_CLANG_SIZE in function bodies on aarch64). CodeCache stores the detected default per library and uses it when DWARF doesn't cover a PC. For unknown libraries (cc==NULL) on Apple/aarch64, fall back to default_clang_frame since Apple ships Clang-only code. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
1 parent 0d2637d commit a1609a3

6 files changed

Lines changed: 36 additions & 10 deletions

File tree

ddprof-lib/src/main/cpp/codeCache.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ CodeCache::CodeCache(const char *name, short lib_index,
6363

6464
_dwarf_table = NULL;
6565
_dwarf_table_length = 0;
66+
_default_frame = &FrameDesc::default_frame;
6667

6768
_capacity = INITIAL_CODE_CACHE_CAPACITY;
6869
_count = 0;
@@ -101,6 +102,7 @@ void CodeCache::copyFrom(const CodeCache& other) {
101102
_dwarf_table = new FrameDesc[_dwarf_table_length];
102103
memcpy(_dwarf_table, other._dwarf_table,
103104
_dwarf_table_length * sizeof(FrameDesc));
105+
_default_frame = other._default_frame;
104106

105107
_capacity = other._capacity;
106108
_count = other._count;
@@ -388,16 +390,15 @@ void CodeCache::makeImportsPatchable() {
388390
}
389391
}
390392

391-
void CodeCache::setDwarfTable(FrameDesc *table, int length) {
393+
void CodeCache::setDwarfTable(FrameDesc *table, int length, const FrameDesc &default_frame) {
392394
_dwarf_table = table;
393395
_dwarf_table_length = length;
396+
_default_frame = &default_frame;
394397
}
395398

396399
FrameDesc CodeCache::findFrameDesc(const void *pc) {
397400
if (_dwarf_table == NULL || _dwarf_table_length == 0) {
398-
// No DWARF data available - use default frame pointer unwinding
399-
// This handles OpenJ9 and other VMs that don't provide DWARF info
400-
return FrameDesc::default_frame;
401+
return *_default_frame;
401402
}
402403

403404
u32 target_loc = (const char *)pc - _text_base;
@@ -420,7 +421,7 @@ FrameDesc CodeCache::findFrameDesc(const void *pc) {
420421
} else if (target_loc - _plt_offset < _plt_size) {
421422
return FrameDesc::empty_frame;
422423
} else {
423-
return FrameDesc::default_frame;
424+
return *_default_frame;
424425
}
425426
}
426427

ddprof-lib/src/main/cpp/codeCache.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "common.h"
1010
#include "counters.h"
11+
#include "dwarf.h"
1112
#include "utils.h"
1213

1314
#include <jvmti.h>
@@ -116,8 +117,6 @@ class CodeBlob {
116117
}
117118
};
118119

119-
class FrameDesc;
120-
121120
class CodeCache {
122121
private:
123122
char *_name;
@@ -141,6 +140,7 @@ class CodeCache {
141140

142141
FrameDesc *_dwarf_table;
143142
int _dwarf_table_length;
143+
const FrameDesc *_default_frame;
144144

145145
int _capacity;
146146
int _count;
@@ -241,7 +241,7 @@ class CodeCache {
241241
void findSymbolsByPrefix(std::vector<const char *> &prefixes,
242242
std::vector<const void *> &symbols);
243243

244-
void setDwarfTable(FrameDesc *table, int length);
244+
void setDwarfTable(FrameDesc *table, int length, const FrameDesc &default_frame = FrameDesc::default_frame);
245245
FrameDesc findFrameDesc(const void *pc);
246246

247247
long long memoryUsage() {

ddprof-lib/src/main/cpp/dwarf.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ DwarfParser::DwarfParser(const char *name, const char *image_base,
104104

105105
_code_align = sizeof(instruction_t);
106106
_data_align = -(int)sizeof(void *);
107+
_linked_frame_size = -1;
107108

108109
parse(eh_frame_hdr);
109110
}
@@ -411,6 +412,14 @@ void DwarfParser::addRecord(u32 loc, u32 cfa_reg, int cfa_off, int fp_off,
411412
// cfa_reg and cfa_off can be encoded to a single 32 bit value, considering the existing and supported systems
412413
u32 cfa = static_cast<u32>(cfa_off) << 8 | static_cast<u32>(cfa_reg & 0xff);
413414

415+
// Detect the linked frame size from the first FP-based entry with a non-zero offset.
416+
// Both GCC and Clang emit DW_REG_FP with cfa_off = LINKED_FRAME_CLANG_SIZE in function
417+
// bodies after the prologue completes. Terminal records use cfa_off = 0 (LINKED_FRAME_SIZE
418+
// on aarch64) and do not influence detection.
419+
if (_linked_frame_size < 0 && cfa_reg == DW_REG_FP && cfa_off > 0) {
420+
_linked_frame_size = cfa_off;
421+
}
422+
414423
if (_prev == NULL || (_prev->loc == loc && --_count >= 0) ||
415424
_prev->cfa != cfa || _prev->fp_off != fp_off || _prev->pc_off != pc_off) {
416425
_prev = addRecordRaw(loc, cfa, fp_off, pc_off);

ddprof-lib/src/main/cpp/dwarf.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ struct FrameDesc {
8080

8181
static FrameDesc empty_frame;
8282
static FrameDesc default_frame;
83-
static FrameDesc default_clang_frame;
83+
static FrameDesc default_clang_frame;
8484
static FrameDesc no_dwarf_frame;
8585

8686
static int comparator(const void* p1, const void* p2) {
@@ -104,6 +104,7 @@ class DwarfParser {
104104

105105
u32 _code_align;
106106
int _data_align;
107+
int _linked_frame_size; // detected from FP-based DWARF entries; -1 = undetected
107108

108109
const char* add(size_t size) {
109110
const char* ptr = _ptr;
@@ -185,6 +186,13 @@ class DwarfParser {
185186
int count() const {
186187
return _count;
187188
}
189+
190+
const FrameDesc& detectedDefaultFrame() const {
191+
if (_linked_frame_size == LINKED_FRAME_CLANG_SIZE && LINKED_FRAME_CLANG_SIZE != LINKED_FRAME_SIZE) {
192+
return FrameDesc::default_clang_frame;
193+
}
194+
return FrameDesc::default_frame;
195+
}
188196
};
189197

190198
#endif // _DWARF_H

ddprof-lib/src/main/cpp/stackWalker.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,11 @@ int StackWalker::walkDwarf(void* ucontext, const void** callchain, int max_depth
158158

159159
uintptr_t prev_sp = sp;
160160
CodeCache* cc = profiler->findLibraryByAddress(pc);
161+
#if defined(__APPLE__) && defined(__aarch64__)
162+
FrameDesc f = cc != NULL ? cc->findFrameDesc(pc) : FrameDesc::default_clang_frame;
163+
#else
161164
FrameDesc f = cc != NULL ? cc->findFrameDesc(pc) : FrameDesc::default_frame;
165+
#endif
162166

163167
u8 cfa_reg = (u8)f.cfa;
164168
int cfa_off = f.cfa >> 8;
@@ -694,7 +698,11 @@ __attribute__((no_sanitize("address"))) int StackWalker::walkVM(void* ucontext,
694698
dwarf_unwind:
695699
uintptr_t prev_sp = sp;
696700
CodeCache* cc = profiler->findLibraryByAddress(pc);
701+
#if defined(__APPLE__) && defined(__aarch64__)
702+
FrameDesc f = cc != NULL ? cc->findFrameDesc(pc) : FrameDesc::default_clang_frame;
703+
#else
697704
FrameDesc f = cc != NULL ? cc->findFrameDesc(pc) : FrameDesc::default_frame;
705+
#endif
698706

699707
u8 cfa_reg = (u8)f.cfa;
700708
int cfa_off = f.cfa >> 8;

ddprof-lib/src/main/cpp/symbols_linux.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,7 @@ void ElfParser::parseDwarfInfo() {
605605
if (eh_frame_hdr != NULL) {
606606
if (eh_frame_hdr->p_vaddr != 0) {
607607
DwarfParser dwarf(_cc->name(), _base, at(eh_frame_hdr));
608-
_cc->setDwarfTable(dwarf.table(), dwarf.count());
608+
_cc->setDwarfTable(dwarf.table(), dwarf.count(), dwarf.detectedDefaultFrame());
609609
} else if (strcmp(_cc->name(), "[vdso]") == 0) {
610610
FrameDesc* table = (FrameDesc*)malloc(sizeof(FrameDesc));
611611
*table = FrameDesc::empty_frame;

0 commit comments

Comments
 (0)