xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/profile/InstrProfilingMerge.c (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
10b57cec5SDimitry Andric /*===- InstrProfilingMerge.c - Profile in-process Merging  ---------------===*\
20b57cec5SDimitry Andric |*
30b57cec5SDimitry Andric |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric |* See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric |*
70b57cec5SDimitry Andric |*===----------------------------------------------------------------------===*
80b57cec5SDimitry Andric |* This file defines the API needed for in-process merging of profile data
90b57cec5SDimitry Andric |* stored in memory buffer.
100b57cec5SDimitry Andric \*===---------------------------------------------------------------------===*/
110b57cec5SDimitry Andric 
120b57cec5SDimitry Andric #include "InstrProfiling.h"
130b57cec5SDimitry Andric #include "InstrProfilingInternal.h"
140b57cec5SDimitry Andric #include "InstrProfilingUtil.h"
150b57cec5SDimitry Andric 
160b57cec5SDimitry Andric #define INSTR_PROF_VALUE_PROF_DATA
17480093f4SDimitry Andric #include "profile/InstrProfData.inc"
180b57cec5SDimitry Andric 
190b57cec5SDimitry Andric COMPILER_RT_VISIBILITY
200b57cec5SDimitry Andric void (*VPMergeHook)(ValueProfData *, __llvm_profile_data *);
210b57cec5SDimitry Andric 
220b57cec5SDimitry Andric COMPILER_RT_VISIBILITY
2381ad6265SDimitry Andric uint64_t lprofGetLoadModuleSignature(void) {
240b57cec5SDimitry Andric   /* A very fast way to compute a module signature.  */
256e75b2fbSDimitry Andric   uint64_t Version = __llvm_profile_get_version();
2604eeddc0SDimitry Andric   uint64_t NumCounters = __llvm_profile_get_num_counters(
2704eeddc0SDimitry Andric       __llvm_profile_begin_counters(), __llvm_profile_end_counters());
2804eeddc0SDimitry Andric   uint64_t NumData = __llvm_profile_get_num_data(__llvm_profile_begin_data(),
290b57cec5SDimitry Andric                                                  __llvm_profile_end_data());
300b57cec5SDimitry Andric   uint64_t NamesSize =
310b57cec5SDimitry Andric       (uint64_t)(__llvm_profile_end_names() - __llvm_profile_begin_names());
320b57cec5SDimitry Andric   uint64_t NumVnodes =
330b57cec5SDimitry Andric       (uint64_t)(__llvm_profile_end_vnodes() - __llvm_profile_begin_vnodes());
340b57cec5SDimitry Andric   const __llvm_profile_data *FirstD = __llvm_profile_begin_data();
350b57cec5SDimitry Andric 
3604eeddc0SDimitry Andric   return (NamesSize << 40) + (NumCounters << 30) + (NumData << 20) +
3704eeddc0SDimitry Andric          (NumVnodes << 10) + (NumData > 0 ? FirstD->NameRef : 0) + Version +
384824e7fdSDimitry Andric          __llvm_profile_get_magic();
390b57cec5SDimitry Andric }
400b57cec5SDimitry Andric 
4106c3fb27SDimitry Andric #ifdef __GNUC__
4206c3fb27SDimitry Andric #pragma GCC diagnostic push
4306c3fb27SDimitry Andric #pragma GCC diagnostic ignored "-Wcast-qual"
447a6dacacSDimitry Andric #elif defined(__clang__)
457a6dacacSDimitry Andric #pragma clang diagnostic push
467a6dacacSDimitry Andric #pragma clang diagnostic ignored "-Wcast-qual"
4706c3fb27SDimitry Andric #endif
4806c3fb27SDimitry Andric 
490b57cec5SDimitry Andric /* Returns 1 if profile is not structurally compatible.  */
500b57cec5SDimitry Andric COMPILER_RT_VISIBILITY
510b57cec5SDimitry Andric int __llvm_profile_check_compatibility(const char *ProfileData,
520b57cec5SDimitry Andric                                        uint64_t ProfileSize) {
530b57cec5SDimitry Andric   __llvm_profile_header *Header = (__llvm_profile_header *)ProfileData;
540b57cec5SDimitry Andric   __llvm_profile_data *SrcDataStart, *SrcDataEnd, *SrcData, *DstData;
550b57cec5SDimitry Andric   SrcDataStart =
566e75b2fbSDimitry Andric       (__llvm_profile_data *)(ProfileData + sizeof(__llvm_profile_header) +
576e75b2fbSDimitry Andric                               Header->BinaryIdsSize);
585f757f3fSDimitry Andric   SrcDataEnd = SrcDataStart + Header->NumData;
590b57cec5SDimitry Andric 
600b57cec5SDimitry Andric   if (ProfileSize < sizeof(__llvm_profile_header))
610b57cec5SDimitry Andric     return 1;
620b57cec5SDimitry Andric 
630b57cec5SDimitry Andric   /* Check the header first.  */
640b57cec5SDimitry Andric   if (Header->Magic != __llvm_profile_get_magic() ||
650b57cec5SDimitry Andric       Header->Version != __llvm_profile_get_version() ||
665f757f3fSDimitry Andric       Header->NumData !=
6704eeddc0SDimitry Andric           __llvm_profile_get_num_data(__llvm_profile_begin_data(),
680b57cec5SDimitry Andric                                       __llvm_profile_end_data()) ||
695f757f3fSDimitry Andric       Header->NumCounters !=
7004eeddc0SDimitry Andric           __llvm_profile_get_num_counters(__llvm_profile_begin_counters(),
7104eeddc0SDimitry Andric                                           __llvm_profile_end_counters()) ||
725f757f3fSDimitry Andric       Header->NumBitmapBytes !=
735f757f3fSDimitry Andric           __llvm_profile_get_num_bitmap_bytes(__llvm_profile_begin_bitmap(),
745f757f3fSDimitry Andric                                               __llvm_profile_end_bitmap()) ||
755f757f3fSDimitry Andric       Header->NamesSize !=
765f757f3fSDimitry Andric           __llvm_profile_get_name_size(__llvm_profile_begin_names(),
775f757f3fSDimitry Andric                                        __llvm_profile_end_names()) ||
780b57cec5SDimitry Andric       Header->ValueKindLast != IPVK_Last)
790b57cec5SDimitry Andric     return 1;
800b57cec5SDimitry Andric 
8104eeddc0SDimitry Andric   if (ProfileSize <
8204eeddc0SDimitry Andric       sizeof(__llvm_profile_header) + Header->BinaryIdsSize +
835f757f3fSDimitry Andric           Header->NumData * sizeof(__llvm_profile_data) + Header->NamesSize +
845f757f3fSDimitry Andric           Header->NumCounters * __llvm_profile_counter_entry_size() +
855f757f3fSDimitry Andric           Header->NumBitmapBytes)
860b57cec5SDimitry Andric     return 1;
870b57cec5SDimitry Andric 
880b57cec5SDimitry Andric   for (SrcData = SrcDataStart,
890b57cec5SDimitry Andric        DstData = (__llvm_profile_data *)__llvm_profile_begin_data();
900b57cec5SDimitry Andric        SrcData < SrcDataEnd; ++SrcData, ++DstData) {
910b57cec5SDimitry Andric     if (SrcData->NameRef != DstData->NameRef ||
920b57cec5SDimitry Andric         SrcData->FuncHash != DstData->FuncHash ||
935f757f3fSDimitry Andric         SrcData->NumCounters != DstData->NumCounters ||
945f757f3fSDimitry Andric         SrcData->NumBitmapBytes != DstData->NumBitmapBytes)
950b57cec5SDimitry Andric       return 1;
960b57cec5SDimitry Andric   }
970b57cec5SDimitry Andric 
980b57cec5SDimitry Andric   /* Matched! */
990b57cec5SDimitry Andric   return 0;
1000b57cec5SDimitry Andric }
1010b57cec5SDimitry Andric 
102349cc55cSDimitry Andric static uintptr_t signextIfWin64(void *V) {
103349cc55cSDimitry Andric #ifdef _WIN64
104349cc55cSDimitry Andric   return (uintptr_t)(int32_t)(uintptr_t)V;
105349cc55cSDimitry Andric #else
106349cc55cSDimitry Andric   return (uintptr_t)V;
107349cc55cSDimitry Andric #endif
108349cc55cSDimitry Andric }
109349cc55cSDimitry Andric 
110*0fca6ea1SDimitry Andric // Skip names section, vtable profile data section and vtable names section
111*0fca6ea1SDimitry Andric // for runtime profile merge. To merge runtime addresses from multiple
112*0fca6ea1SDimitry Andric // profiles collected from the same instrumented binary, the binary should be
113*0fca6ea1SDimitry Andric // loaded at fixed base address (e.g., build with -no-pie, or run with ASLR
114*0fca6ea1SDimitry Andric // disabled). In this set-up these three sections remain unchanged.
115*0fca6ea1SDimitry Andric static uint64_t
116*0fca6ea1SDimitry Andric getDistanceFromCounterToValueProf(const __llvm_profile_header *const Header) {
117*0fca6ea1SDimitry Andric   const uint64_t VTableSectionSize =
118*0fca6ea1SDimitry Andric       Header->NumVTables * sizeof(VTableProfData);
119*0fca6ea1SDimitry Andric   const uint64_t PaddingBytesAfterVTableSection =
120*0fca6ea1SDimitry Andric       __llvm_profile_get_num_padding_bytes(VTableSectionSize);
121*0fca6ea1SDimitry Andric   const uint64_t VNamesSize = Header->VNamesSize;
122*0fca6ea1SDimitry Andric   const uint64_t PaddingBytesAfterVNamesSize =
123*0fca6ea1SDimitry Andric       __llvm_profile_get_num_padding_bytes(VNamesSize);
124*0fca6ea1SDimitry Andric   return Header->NamesSize +
125*0fca6ea1SDimitry Andric          __llvm_profile_get_num_padding_bytes(Header->NamesSize) +
126*0fca6ea1SDimitry Andric          VTableSectionSize + PaddingBytesAfterVTableSection + VNamesSize +
127*0fca6ea1SDimitry Andric          PaddingBytesAfterVNamesSize;
128*0fca6ea1SDimitry Andric }
129*0fca6ea1SDimitry Andric 
1300b57cec5SDimitry Andric COMPILER_RT_VISIBILITY
131fe6060f1SDimitry Andric int __llvm_profile_merge_from_buffer(const char *ProfileData,
1320b57cec5SDimitry Andric                                      uint64_t ProfileSize) {
1335f757f3fSDimitry Andric   if (__llvm_profile_get_version() & VARIANT_MASK_TEMPORAL_PROF) {
1345f757f3fSDimitry Andric     PROF_ERR("%s\n",
1355f757f3fSDimitry Andric              "Temporal profiles do not support profile merging at runtime. "
1360eae32dcSDimitry Andric              "Instead, merge raw profiles using the llvm-profdata tool.");
1370eae32dcSDimitry Andric     return 1;
1380eae32dcSDimitry Andric   }
1390eae32dcSDimitry Andric 
1400b57cec5SDimitry Andric   __llvm_profile_data *SrcDataStart, *SrcDataEnd, *SrcData, *DstData;
1410b57cec5SDimitry Andric   __llvm_profile_header *Header = (__llvm_profile_header *)ProfileData;
1425f757f3fSDimitry Andric   char *SrcCountersStart, *DstCounter;
1435f757f3fSDimitry Andric   const char *SrcCountersEnd, *SrcCounter;
1445f757f3fSDimitry Andric   const char *SrcBitmapStart;
1450b57cec5SDimitry Andric   const char *SrcNameStart;
146fe6060f1SDimitry Andric   const char *SrcValueProfDataStart, *SrcValueProfData;
147349cc55cSDimitry Andric   uintptr_t CountersDelta = Header->CountersDelta;
1485f757f3fSDimitry Andric   uintptr_t BitmapDelta = Header->BitmapDelta;
1490b57cec5SDimitry Andric 
1500b57cec5SDimitry Andric   SrcDataStart =
1516e75b2fbSDimitry Andric       (__llvm_profile_data *)(ProfileData + sizeof(__llvm_profile_header) +
1526e75b2fbSDimitry Andric                               Header->BinaryIdsSize);
1535f757f3fSDimitry Andric   SrcDataEnd = SrcDataStart + Header->NumData;
15404eeddc0SDimitry Andric   SrcCountersStart = (char *)SrcDataEnd;
1555f757f3fSDimitry Andric   SrcCountersEnd = SrcCountersStart +
1565f757f3fSDimitry Andric                    Header->NumCounters * __llvm_profile_counter_entry_size();
1575f757f3fSDimitry Andric   SrcBitmapStart = SrcCountersEnd;
1585f757f3fSDimitry Andric   SrcNameStart = SrcBitmapStart + Header->NumBitmapBytes;
1590b57cec5SDimitry Andric   SrcValueProfDataStart =
160*0fca6ea1SDimitry Andric       SrcNameStart + getDistanceFromCounterToValueProf(Header);
1615f757f3fSDimitry Andric   if (SrcNameStart < SrcCountersStart || SrcNameStart < SrcBitmapStart)
162fe6060f1SDimitry Andric     return 1;
1630b57cec5SDimitry Andric 
1645f757f3fSDimitry Andric   // Merge counters by iterating the entire counter section when data section is
1655f757f3fSDimitry Andric   // empty due to correlation.
1665f757f3fSDimitry Andric   if (Header->NumData == 0) {
1675f757f3fSDimitry Andric     for (SrcCounter = SrcCountersStart,
1685f757f3fSDimitry Andric         DstCounter = __llvm_profile_begin_counters();
1695f757f3fSDimitry Andric          SrcCounter < SrcCountersEnd;) {
1705f757f3fSDimitry Andric       if (__llvm_profile_get_version() & VARIANT_MASK_BYTE_COVERAGE) {
1715f757f3fSDimitry Andric         *DstCounter &= *SrcCounter;
1725f757f3fSDimitry Andric       } else {
1735f757f3fSDimitry Andric         *(uint64_t *)DstCounter += *(uint64_t *)SrcCounter;
1745f757f3fSDimitry Andric       }
1755f757f3fSDimitry Andric       SrcCounter += __llvm_profile_counter_entry_size();
1765f757f3fSDimitry Andric       DstCounter += __llvm_profile_counter_entry_size();
1775f757f3fSDimitry Andric     }
1785f757f3fSDimitry Andric     return 0;
1795f757f3fSDimitry Andric   }
1805f757f3fSDimitry Andric 
1810b57cec5SDimitry Andric   for (SrcData = SrcDataStart,
1820b57cec5SDimitry Andric       DstData = (__llvm_profile_data *)__llvm_profile_begin_data(),
1830b57cec5SDimitry Andric       SrcValueProfData = SrcValueProfDataStart;
1840b57cec5SDimitry Andric        SrcData < SrcDataEnd; ++SrcData, ++DstData) {
185349cc55cSDimitry Andric     // For the in-memory destination, CounterPtr is the distance from the start
186349cc55cSDimitry Andric     // address of the data to the start address of the counter. On WIN64,
187349cc55cSDimitry Andric     // CounterPtr is a truncated 32-bit value due to COFF limitation. Sign
188349cc55cSDimitry Andric     // extend CounterPtr to get the original value.
18904eeddc0SDimitry Andric     char *DstCounters =
19004eeddc0SDimitry Andric         (char *)((uintptr_t)DstData + signextIfWin64(DstData->CounterPtr));
1915f757f3fSDimitry Andric     char *DstBitmap =
1925f757f3fSDimitry Andric         (char *)((uintptr_t)DstData + signextIfWin64(DstData->BitmapPtr));
193fe6060f1SDimitry Andric     unsigned NVK = 0;
1940b57cec5SDimitry Andric 
195349cc55cSDimitry Andric     // SrcData is a serialized representation of the memory image. We need to
196349cc55cSDimitry Andric     // compute the in-buffer counter offset from the in-memory address distance.
197349cc55cSDimitry Andric     // The initial CountersDelta is the in-memory address difference
198349cc55cSDimitry Andric     // start(__llvm_prf_cnts)-start(__llvm_prf_data), so SrcData->CounterPtr -
199349cc55cSDimitry Andric     // CountersDelta computes the offset into the in-buffer counter section.
200349cc55cSDimitry Andric     //
201349cc55cSDimitry Andric     // On WIN64, CountersDelta is truncated as well, so no need for signext.
20204eeddc0SDimitry Andric     char *SrcCounters =
20304eeddc0SDimitry Andric         SrcCountersStart + ((uintptr_t)SrcData->CounterPtr - CountersDelta);
204349cc55cSDimitry Andric     // CountersDelta needs to be decreased as we advance to the next data
205349cc55cSDimitry Andric     // record.
206349cc55cSDimitry Andric     CountersDelta -= sizeof(*SrcData);
207fe6060f1SDimitry Andric     unsigned NC = SrcData->NumCounters;
208fe6060f1SDimitry Andric     if (NC == 0)
209fe6060f1SDimitry Andric       return 1;
21004eeddc0SDimitry Andric     if (SrcCounters < SrcCountersStart || SrcCounters >= SrcNameStart ||
21104eeddc0SDimitry Andric         (SrcCounters + __llvm_profile_counter_entry_size() * NC) > SrcNameStart)
212fe6060f1SDimitry Andric       return 1;
2131fd87a68SDimitry Andric     for (unsigned I = 0; I < NC; I++) {
2141fd87a68SDimitry Andric       if (__llvm_profile_get_version() & VARIANT_MASK_BYTE_COVERAGE) {
2151fd87a68SDimitry Andric         // A value of zero signifies the function is covered.
2161fd87a68SDimitry Andric         DstCounters[I] &= SrcCounters[I];
2171fd87a68SDimitry Andric       } else {
21804eeddc0SDimitry Andric         ((uint64_t *)DstCounters)[I] += ((uint64_t *)SrcCounters)[I];
2191fd87a68SDimitry Andric       }
2201fd87a68SDimitry Andric     }
2210b57cec5SDimitry Andric 
2225f757f3fSDimitry Andric     const char *SrcBitmap =
2235f757f3fSDimitry Andric         SrcBitmapStart + ((uintptr_t)SrcData->BitmapPtr - BitmapDelta);
2245f757f3fSDimitry Andric     // BitmapDelta also needs to be decreased as we advance to the next data
2255f757f3fSDimitry Andric     // record.
2265f757f3fSDimitry Andric     BitmapDelta -= sizeof(*SrcData);
2275f757f3fSDimitry Andric     unsigned NB = SrcData->NumBitmapBytes;
2285f757f3fSDimitry Andric     // NumBitmapBytes may legitimately be 0. Just keep going.
2295f757f3fSDimitry Andric     if (NB != 0) {
2305f757f3fSDimitry Andric       if (SrcBitmap < SrcBitmapStart || (SrcBitmap + NB) > SrcNameStart)
2315f757f3fSDimitry Andric         return 1;
2325f757f3fSDimitry Andric       // Merge Src and Dst Bitmap bytes by simply ORing them together.
2335f757f3fSDimitry Andric       for (unsigned I = 0; I < NB; I++)
2345f757f3fSDimitry Andric         DstBitmap[I] |= SrcBitmap[I];
2355f757f3fSDimitry Andric     }
2365f757f3fSDimitry Andric 
2370b57cec5SDimitry Andric     /* Now merge value profile data. */
2380b57cec5SDimitry Andric     if (!VPMergeHook)
2390b57cec5SDimitry Andric       continue;
2400b57cec5SDimitry Andric 
241fe6060f1SDimitry Andric     for (unsigned I = 0; I <= IPVK_Last; I++)
2420b57cec5SDimitry Andric       NVK += (SrcData->NumValueSites[I] != 0);
2430b57cec5SDimitry Andric 
2440b57cec5SDimitry Andric     if (!NVK)
2450b57cec5SDimitry Andric       continue;
2460b57cec5SDimitry Andric 
247fe6060f1SDimitry Andric     if (SrcValueProfData >= ProfileData + ProfileSize)
248fe6060f1SDimitry Andric       return 1;
249fe6060f1SDimitry Andric     VPMergeHook((ValueProfData *)SrcValueProfData, DstData);
250fe6060f1SDimitry Andric     SrcValueProfData =
251fe6060f1SDimitry Andric         SrcValueProfData + ((ValueProfData *)SrcValueProfData)->TotalSize;
2520b57cec5SDimitry Andric   }
253fe6060f1SDimitry Andric 
254fe6060f1SDimitry Andric   return 0;
2550b57cec5SDimitry Andric }
25606c3fb27SDimitry Andric 
25706c3fb27SDimitry Andric #ifdef __GNUC__
25806c3fb27SDimitry Andric #pragma GCC diagnostic pop
2597a6dacacSDimitry Andric #elif defined(__clang__)
2607a6dacacSDimitry Andric #pragma clang diagnostic pop
26106c3fb27SDimitry Andric #endif
262