@@ -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
13991421void 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
16581708void Recording::writeLogLevels (Buffer *buf) {
0 commit comments