1 /*===- InstrProfilingMerge.c - Profile in-process Merging  ---------------===*\
2 |*
3 |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 |* See https://llvm.org/LICENSE.txt for license information.
5 |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 |*
7 |*===----------------------------------------------------------------------===*
8 |* This file defines the API needed for in-process merging of profile data
9 |* stored in memory buffer.
10 \*===---------------------------------------------------------------------===*/
11 
12 #include "InstrProfiling.h"
13 #include "InstrProfilingInternal.h"
14 #include "InstrProfilingUtil.h"
15 
16 #define INSTR_PROF_VALUE_PROF_DATA
17 #include "profile/InstrProfData.inc"
18 
19 COMPILER_RT_VISIBILITY
20 void (*VPMergeHook)(ValueProfData *, __llvm_profile_data *);
21 
22 COMPILER_RT_VISIBILITY
23 uint64_t lprofGetLoadModuleSignature() {
24   /* A very fast way to compute a module signature.  */
25   uint64_t CounterSize = (uint64_t)(__llvm_profile_end_counters() -
26                                     __llvm_profile_begin_counters());
27   uint64_t DataSize = __llvm_profile_get_data_size(__llvm_profile_begin_data(),
28                                                    __llvm_profile_end_data());
29   uint64_t NamesSize =
30       (uint64_t)(__llvm_profile_end_names() - __llvm_profile_begin_names());
31   uint64_t NumVnodes =
32       (uint64_t)(__llvm_profile_end_vnodes() - __llvm_profile_begin_vnodes());
33   const __llvm_profile_data *FirstD = __llvm_profile_begin_data();
34 
35   return (NamesSize << 40) + (CounterSize << 30) + (DataSize << 20) +
36          (NumVnodes << 10) + (DataSize > 0 ? FirstD->NameRef : 0);
37 }
38 
39 /* Returns 1 if profile is not structurally compatible.  */
40 COMPILER_RT_VISIBILITY
41 int __llvm_profile_check_compatibility(const char *ProfileData,
42                                        uint64_t ProfileSize) {
43   /* Check profile header only for now  */
44   __llvm_profile_header *Header = (__llvm_profile_header *)ProfileData;
45   __llvm_profile_data *SrcDataStart, *SrcDataEnd, *SrcData, *DstData;
46   SrcDataStart =
47       (__llvm_profile_data *)(ProfileData + sizeof(__llvm_profile_header));
48   SrcDataEnd = SrcDataStart + Header->DataSize;
49 
50   if (ProfileSize < sizeof(__llvm_profile_header))
51     return 1;
52 
53   /* Check the header first.  */
54   if (Header->Magic != __llvm_profile_get_magic() ||
55       Header->Version != __llvm_profile_get_version() ||
56       Header->DataSize !=
57           __llvm_profile_get_data_size(__llvm_profile_begin_data(),
58                                        __llvm_profile_end_data()) ||
59       Header->CountersSize != (uint64_t)(__llvm_profile_end_counters() -
60                                          __llvm_profile_begin_counters()) ||
61       Header->NamesSize != (uint64_t)(__llvm_profile_end_names() -
62                                       __llvm_profile_begin_names()) ||
63       Header->ValueKindLast != IPVK_Last)
64     return 1;
65 
66   if (ProfileSize < sizeof(__llvm_profile_header) +
67                         Header->DataSize * sizeof(__llvm_profile_data) +
68                         Header->NamesSize + Header->CountersSize)
69     return 1;
70 
71   for (SrcData = SrcDataStart,
72        DstData = (__llvm_profile_data *)__llvm_profile_begin_data();
73        SrcData < SrcDataEnd; ++SrcData, ++DstData) {
74     if (SrcData->NameRef != DstData->NameRef ||
75         SrcData->FuncHash != DstData->FuncHash ||
76         SrcData->NumCounters != DstData->NumCounters)
77       return 1;
78   }
79 
80   /* Matched! */
81   return 0;
82 }
83 
84 static uintptr_t signextIfWin64(void *V) {
85 #ifdef _WIN64
86   return (uintptr_t)(int32_t)(uintptr_t)V;
87 #else
88   return (uintptr_t)V;
89 #endif
90 }
91 
92 COMPILER_RT_VISIBILITY
93 int __llvm_profile_merge_from_buffer(const char *ProfileData,
94                                      uint64_t ProfileSize) {
95   __llvm_profile_data *SrcDataStart, *SrcDataEnd, *SrcData, *DstData;
96   __llvm_profile_header *Header = (__llvm_profile_header *)ProfileData;
97   uint64_t *SrcCountersStart;
98   const char *SrcNameStart;
99   const char *SrcValueProfDataStart, *SrcValueProfData;
100   uintptr_t CountersDelta = Header->CountersDelta;
101 
102   SrcDataStart =
103       (__llvm_profile_data *)(ProfileData + sizeof(__llvm_profile_header));
104   SrcDataEnd = SrcDataStart + Header->DataSize;
105   SrcCountersStart = (uint64_t *)SrcDataEnd;
106   SrcNameStart = (const char *)(SrcCountersStart + Header->CountersSize);
107   SrcValueProfDataStart =
108       SrcNameStart + Header->NamesSize +
109       __llvm_profile_get_num_padding_bytes(Header->NamesSize);
110   if (SrcNameStart < (const char *)SrcCountersStart)
111     return 1;
112 
113   for (SrcData = SrcDataStart,
114       DstData = (__llvm_profile_data *)__llvm_profile_begin_data(),
115       SrcValueProfData = SrcValueProfDataStart;
116        SrcData < SrcDataEnd; ++SrcData, ++DstData) {
117     // For the in-memory destination, CounterPtr is the distance from the start
118     // address of the data to the start address of the counter. On WIN64,
119     // CounterPtr is a truncated 32-bit value due to COFF limitation. Sign
120     // extend CounterPtr to get the original value.
121     uint64_t *DstCounters =
122         (uint64_t *)((uintptr_t)DstData + signextIfWin64(DstData->CounterPtr));
123     unsigned NVK = 0;
124 
125     // SrcData is a serialized representation of the memory image. We need to
126     // compute the in-buffer counter offset from the in-memory address distance.
127     // The initial CountersDelta is the in-memory address difference
128     // start(__llvm_prf_cnts)-start(__llvm_prf_data), so SrcData->CounterPtr -
129     // CountersDelta computes the offset into the in-buffer counter section.
130     //
131     // On WIN64, CountersDelta is truncated as well, so no need for signext.
132     uint64_t *SrcCounters =
133         SrcCountersStart +
134         ((uintptr_t)SrcData->CounterPtr - CountersDelta) / sizeof(uint64_t);
135     // CountersDelta needs to be decreased as we advance to the next data
136     // record.
137     CountersDelta -= sizeof(*SrcData);
138     unsigned NC = SrcData->NumCounters;
139     if (NC == 0)
140       return 1;
141     if (SrcCounters < SrcCountersStart ||
142         (const char *)SrcCounters >= SrcNameStart ||
143         (const char *)(SrcCounters + NC) > SrcNameStart)
144       return 1;
145     for (unsigned I = 0; I < NC; I++)
146       DstCounters[I] += SrcCounters[I];
147 
148     /* Now merge value profile data. */
149     if (!VPMergeHook)
150       continue;
151 
152     for (unsigned I = 0; I <= IPVK_Last; I++)
153       NVK += (SrcData->NumValueSites[I] != 0);
154 
155     if (!NVK)
156       continue;
157 
158     if (SrcValueProfData >= ProfileData + ProfileSize)
159       return 1;
160     VPMergeHook((ValueProfData *)SrcValueProfData, DstData);
161     SrcValueProfData =
162         SrcValueProfData + ((ValueProfData *)SrcValueProfData)->TotalSize;
163   }
164 
165   return 0;
166 }
167