Skip to content

Commit 48d5522

Browse files
authored
fix(jfr): skip emitting empty constant pools (#603)
1 parent 0e505e4 commit 48d5522

3 files changed

Lines changed: 462 additions & 38 deletions

File tree

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

Lines changed: 77 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -761,7 +761,8 @@ off_t Recording::finishChunk(bool end_recording, bool do_cleanup) {
761761
}
762762

763763
off_t cpool_offset = lseek(_fd, 0, SEEK_CUR);
764-
writeCpool(_buf);
764+
int count_offset_in_cpool = 0;
765+
int pool_count = writeCpool(_buf, &count_offset_in_cpool);
765766
flush(_buf);
766767

767768
off_t cpool_end = lseek(_fd, 0, SEEK_CUR);
@@ -771,6 +772,13 @@ off_t Recording::finishChunk(bool end_recording, bool do_cleanup) {
771772
ssize_t result = pwrite(_fd, _buf->data(), 5, cpool_offset);
772773
(void)result;
773774

775+
// Patch the constant pool count placeholder (written as a 1-byte put8 in
776+
// writeCpool). Done flush-safe via pwrite to the FILE offset, mirroring the
777+
// size patch above: _buf has been flushed/reset, so _buf->data() is scratch.
778+
_buf->put8(0, (char)pool_count);
779+
result = pwrite(_fd, _buf->data(), 1, cpool_offset + count_offset_in_cpool);
780+
(void)result;
781+
774782
off_t chunk_end = lseek(_fd, 0, SEEK_CUR);
775783

776784
// // Workaround for JDK-8191415: compute actual TSC frequency, in case JFR is
@@ -1359,15 +1367,24 @@ void Recording::writeNativeLibraries(Buffer *buf) {
13591367
_recorded_lib_count = native_lib_count;
13601368
}
13611369

1362-
void Recording::writeCpool(Buffer *buf) {
1370+
int Recording::writeCpool(Buffer *buf, int *count_offset_in_cpool) {
1371+
// Offset of the cpool start within the buffer. The header below is tiny and
1372+
// flush-free, so the placeholder offset captured relative to this start is a
1373+
// stable cpool-relative offset usable for a flush-safe back-patch by the
1374+
// caller (mirrors the cpool SIZE patch).
1375+
int cpool_start = buf->offset();
13631376
buf->skip(5); // size will be patched later
13641377
buf->putVar64(T_CPOOL);
13651378
buf->putVar64(_start_ticks);
13661379
buf->put8(0);
13671380
buf->put8(0);
13681381
buf->put8(1);
1369-
// constant pool count - bump each time a new pool is added
1370-
buf->put8(12);
1382+
// Constant pool count. We cannot precompute it: the Method/Class/Package/Symbol
1383+
// pools are only fully populated as a side effect of writeStackTraces/writeMethods
1384+
// (fillJavaMethodInfo), and empty variable pools are skipped entirely. Write a
1385+
// 1-byte placeholder here and back-patch it flush-safe in the caller.
1386+
*count_offset_in_cpool = buf->offset() - cpool_start;
1387+
buf->put8(0);
13711388

13721389
// Profiler::rotateDictsAndRun() rotates the three dictionaries before this
13731390
// path runs, so classMap()->standby() returns an old-active snapshot stable
@@ -1379,21 +1396,26 @@ void Recording::writeCpool(Buffer *buf) {
13791396
// standby() captures the pre-rotation state which writeClasses extends.
13801397
Lookup lookup(this, &_method_map, Profiler::instance()->classMap());
13811398
lookup.initClassCache();
1399+
// CONSTANT pools: always non-empty, always emitted -> 5 sections.
1400+
// writeThreads always emits: it inserts _tid unconditionally before checking.
13821401
writeFrameTypes(buf);
13831402
writeThreadStates(buf);
13841403
writeExecutionModes(buf);
1385-
writeThreads(buf);
1386-
writeStackTraces(buf, &lookup);
1387-
writeMethods(buf, &lookup);
1388-
writeClasses(buf, &lookup);
1389-
writePackages(buf, &lookup);
1390-
writeConstantPoolSection(buf, T_SYMBOL, &lookup._symbols);
1391-
writeConstantPoolSection(buf, T_STRING,
1392-
Profiler::instance()->stringLabelMap()->standby());
1393-
writeConstantPoolSection(buf, T_ATTRIBUTE_VALUE,
1394-
Profiler::instance()->contextValueMap()->standby());
13951404
writeLogLevels(buf);
1405+
writeThreads(buf);
1406+
int pool_count = 5;
1407+
// VARIABLE pools: each returns 1 if emitted, 0 if empty (and thus skipped).
1408+
pool_count += writeStackTraces(buf, &lookup);
1409+
pool_count += writeMethods(buf, &lookup);
1410+
pool_count += writeClasses(buf, &lookup);
1411+
pool_count += writePackages(buf, &lookup);
1412+
pool_count += writeConstantPoolSection(buf, T_SYMBOL, &lookup._symbols);
1413+
pool_count += writeConstantPoolSection(
1414+
buf, T_STRING, Profiler::instance()->stringLabelMap()->standby());
1415+
pool_count += writeConstantPoolSection(
1416+
buf, T_ATTRIBUTE_VALUE, Profiler::instance()->contextValueMap()->standby());
13961417
flushIfNeeded(buf);
1418+
return pool_count;
13971419
}
13981420

13991421
void Recording::writeFrameTypes(Buffer *buf) {
@@ -1466,7 +1488,7 @@ void Recording::writeThreads(Buffer *buf) {
14661488
// We flush from old_index (the previous active set)
14671489

14681490
std::unordered_set<int> threads;
1469-
threads.insert(_tid);
1491+
threads.insert(_tid); // always present: the recording thread itself
14701492

14711493
for (int i = 0; i < CONCURRENCY_LEVEL; ++i) {
14721494
// Collect thread IDs from the fixed-size table into the main set
@@ -1512,14 +1534,22 @@ void Recording::writeThreads(Buffer *buf) {
15121534
}
15131535
}
15141536

1515-
void Recording::writeStackTraces(Buffer *buf, Lookup *lookup) {
1537+
int Recording::writeStackTraces(Buffer *buf, Lookup *lookup) {
15161538
// Reset all referenced flags before processing
15171539
for (MethodMap::iterator it = _method_map.begin(); it != _method_map.end(); ++it) {
15181540
it->second._referenced = false;
15191541
}
15201542

1543+
// Tracks how many traces were written so the empty pool can be skipped.
1544+
// Note: even with zero traces, the methods marking pass below must still run
1545+
// via processCallTraces, but no T_STACK_TRACE section is emitted in that case.
1546+
int trace_count = 0;
15211547
// Use safe trace processing with guaranteed lifetime during callback execution
1522-
Profiler::instance()->processCallTraces([this, buf, lookup](const std::unordered_set<CallTrace*>& traces) {
1548+
Profiler::instance()->processCallTraces([this, buf, lookup, &trace_count](const std::unordered_set<CallTrace*>& traces) {
1549+
if (traces.empty()) {
1550+
return;
1551+
}
1552+
trace_count = (int)traces.size();
15231553
buf->putVar64(T_STACK_TRACE);
15241554
buf->putVar64(traces.size());
15251555
for (std::unordered_set<CallTrace *>::const_iterator it = traces.begin();
@@ -1558,9 +1588,10 @@ void Recording::writeStackTraces(Buffer *buf, Lookup *lookup) {
15581588
flushIfNeeded(buf);
15591589
}
15601590
}); // End of processCallTraces lambda
1591+
return trace_count > 0 ? 1 : 0;
15611592
}
15621593

1563-
void Recording::writeMethods(Buffer *buf, Lookup *lookup) {
1594+
int Recording::writeMethods(Buffer *buf, Lookup *lookup) {
15641595
MethodMap *method_map = lookup->_method_map;
15651596

15661597
u32 marked_count = 0;
@@ -1571,6 +1602,10 @@ void Recording::writeMethods(Buffer *buf, Lookup *lookup) {
15711602
}
15721603
}
15731604

1605+
if (marked_count == 0) {
1606+
return 0;
1607+
}
1608+
15741609
buf->putVar64(T_METHOD);
15751610
buf->putVar64(marked_count);
15761611
for (MethodMap::iterator it = method_map->begin(); it != method_map->end();
@@ -1587,9 +1622,10 @@ void Recording::writeMethods(Buffer *buf, Lookup *lookup) {
15871622
flushIfNeeded(buf);
15881623
}
15891624
}
1625+
return 1;
15901626
}
15911627

1592-
void Recording::writeClasses(Buffer *buf, Lookup *lookup) {
1628+
int Recording::writeClasses(Buffer *buf, Lookup *lookup) {
15931629
DEBUG_ASSERT_NOT_IN_SIGNAL();
15941630
std::map<u32, const char *> classes;
15951631
// standby() returns the dump buffer — the stable snapshot captured by
@@ -1598,6 +1634,10 @@ void Recording::writeClasses(Buffer *buf, Lookup *lookup) {
15981634
// cross-thread writers via waitForRefCountToClear() before returning.
15991635
lookup->_classes->standby()->collect(classes);
16001636

1637+
if (classes.empty()) {
1638+
return 0;
1639+
}
1640+
16011641
buf->putVar64(T_CLASS);
16021642
buf->putVar64(classes.size());
16031643
for (std::map<u32, const char *>::const_iterator it = classes.begin();
@@ -1610,12 +1650,17 @@ void Recording::writeClasses(Buffer *buf, Lookup *lookup) {
16101650
buf->putVar64(0); // access flags
16111651
flushIfNeeded(buf);
16121652
}
1653+
return 1;
16131654
}
16141655

1615-
void Recording::writePackages(Buffer *buf, Lookup *lookup) {
1656+
int Recording::writePackages(Buffer *buf, Lookup *lookup) {
16161657
std::map<u32, const char *> packages;
16171658
lookup->_packages.collect(packages);
16181659

1660+
if (packages.empty()) {
1661+
return 0;
1662+
}
1663+
16191664
buf->putVar32(T_PACKAGE);
16201665
buf->putVar32(packages.size());
16211666
for (std::map<u32, const char *>::const_iterator it = packages.begin();
@@ -1624,10 +1669,14 @@ void Recording::writePackages(Buffer *buf, Lookup *lookup) {
16241669
buf->putVar64(lookup->getSymbol(it->second));
16251670
flushIfNeeded(buf);
16261671
}
1672+
return 1;
16271673
}
16281674

1629-
void Recording::writeConstantPoolSection(
1675+
int Recording::writeConstantPoolSection(
16301676
Buffer *buf, JfrType type, std::map<u32, const char *> &constants) {
1677+
if (constants.empty()) {
1678+
return 0;
1679+
}
16311680
flushIfNeeded(buf);
16321681
buf->putVar64(type);
16331682
buf->putVar64(constants.size());
@@ -1639,20 +1688,21 @@ void Recording::writeConstantPoolSection(
16391688
buf->putVar64(it->first);
16401689
buf->putUtf8(it->second, length);
16411690
}
1691+
return 1;
16421692
}
16431693

1644-
void Recording::writeConstantPoolSection(Buffer *buf, JfrType type,
1645-
Dictionary *dictionary) {
1694+
int Recording::writeConstantPoolSection(Buffer *buf, JfrType type,
1695+
Dictionary *dictionary) {
16461696
std::map<u32, const char *> constants;
16471697
dictionary->collect(constants);
1648-
writeConstantPoolSection(buf, type, constants);
1698+
return writeConstantPoolSection(buf, type, constants);
16491699
}
16501700

1651-
void Recording::writeConstantPoolSection(Buffer *buf, JfrType type,
1652-
StringDictionaryBuffer *buffer) {
1701+
int Recording::writeConstantPoolSection(Buffer *buf, JfrType type,
1702+
StringDictionaryBuffer *buffer) {
16531703
std::map<u32, const char *> constants;
16541704
buffer->collect(constants);
1655-
writeConstantPoolSection(buf, type, constants);
1705+
return writeConstantPoolSection(buf, type, constants);
16561706
}
16571707

16581708
void Recording::writeLogLevels(Buffer *buf) {

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

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -287,30 +287,37 @@ class Recording {
287287
void writeJvmInfo(Buffer *buf);
288288
void writeSystemProperties(Buffer *buf);
289289
void writeNativeLibraries(Buffer *buf);
290-
void writeCpool(Buffer *buf);
290+
// Writes the cpool checkpoint. Returns the number of pool sections actually
291+
// emitted (empty variable pools are skipped) and reports the byte offset of
292+
// the pool-count placeholder within the cpool via *count_offset_in_cpool, so
293+
// the caller can back-patch it flush-safe alongside the cpool size field.
294+
int writeCpool(Buffer *buf, int *count_offset_in_cpool);
291295

292296
void writeFrameTypes(Buffer *buf);
293297

294298
void writeThreadStates(Buffer *buf);
295299

296300
void writeExecutionModes(Buffer *buf);
301+
// writeThreads always emits: _tid is inserted unconditionally so the thread
302+
// pool is never empty. The following variable-pool writers return 1 if a
303+
// section was emitted, 0 if the pool was empty and skipped.
297304
void writeThreads(Buffer *buf);
298305

299-
void writeStackTraces(Buffer *buf, Lookup *lookup);
306+
int writeStackTraces(Buffer *buf, Lookup *lookup);
300307

301-
void writeMethods(Buffer *buf, Lookup *lookup);
308+
int writeMethods(Buffer *buf, Lookup *lookup);
302309

303-
void writeClasses(Buffer *buf, Lookup *lookup);
310+
int writeClasses(Buffer *buf, Lookup *lookup);
304311

305-
void writePackages(Buffer *buf, Lookup *lookup);
312+
int writePackages(Buffer *buf, Lookup *lookup);
306313

307-
void writeConstantPoolSection(Buffer *buf, JfrType type,
308-
std::map<u32, const char *> &constants);
314+
int writeConstantPoolSection(Buffer *buf, JfrType type,
315+
std::map<u32, const char *> &constants);
309316

310-
void writeConstantPoolSection(Buffer *buf, JfrType type,
311-
Dictionary *dictionary);
312-
void writeConstantPoolSection(Buffer *buf, JfrType type,
313-
StringDictionaryBuffer *buffer);
317+
int writeConstantPoolSection(Buffer *buf, JfrType type,
318+
Dictionary *dictionary);
319+
int writeConstantPoolSection(Buffer *buf, JfrType type,
320+
StringDictionaryBuffer *buffer);
314321

315322
void writeLogLevels(Buffer *buf);
316323

0 commit comments

Comments
 (0)