Skip to content

Commit 645f5b1

Browse files
authored
Add support for -fcoverage-mapping support (#24160)
Main introduction is porting `llvm-project/compiler_rt/lib/profile` `into system/lib/compiler_rt/lib/profile` Related #13072 llvm/llvm-project#111332 No modifications were required for what was ported. llvm-cov worked fine for me with simple project. * `-g` is required to produce a properly instrumented binary `error: failed to load coverage: 'my.wasm': no coverage data found`
1 parent 0abde2e commit 645f5b1

35 files changed

+7421
-3
lines changed

ChangeLog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ See docs/process.md for more on how version tagging works.
2020

2121
4.0.11 (in development)
2222
-----------------------
23+
- Add support for [Source-based Code Coverage](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html)
24+
To build with coverage enabled use `-fprofile-instr-generate -fcoverage-mapping`. (#24160)
2325
- The `ENVIRONMENT` setting will now be automatically updated to include
2426
`worker` if multi-threading is enabled. (#24525)
2527
- Removed the `HEADLESS` option. It tried to simulate a minimal browser-like

system/lib/compiler-rt/include/profile/InstrProfData.inc

Lines changed: 1023 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*===-- MemEntryDef.inc - MemProf profiling runtime macros -*- C++ -*-======== *\
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+
/*
9+
* This file defines the macros for memprof profiling data structures.
10+
* Eg. usage to define the memprof meminfoblock struct:
11+
*
12+
* struct MemInfoBlock {
13+
* #define MIBEntryDef(NameTag, Name, Type) Type Name;
14+
* #include MIBEntryDef.inc
15+
* #undef MIBEntryDef
16+
* };
17+
*
18+
* This file has two identical copies. The primary copy lives in LLVM and
19+
* the other one sits in compiler-rt/include/profile directory. To make changes
20+
* in this file, first modify the primary copy and copy it over to compiler-rt.
21+
* Testing of any change in this file can start only after the two copies are
22+
* synced up.
23+
*
24+
\*===----------------------------------------------------------------------===*/
25+
#ifndef MIBEntryDef
26+
#define MIBEntryDef(NameTag, Name, Type)
27+
#endif
28+
29+
MIBEntryDef(AllocCount = 1, AllocCount, uint32_t)
30+
MIBEntryDef(TotalAccessCount = 2, TotalAccessCount, uint64_t)
31+
MIBEntryDef(MinAccessCount = 3, MinAccessCount, uint64_t)
32+
MIBEntryDef(MaxAccessCount = 4, MaxAccessCount, uint64_t)
33+
MIBEntryDef(TotalSize = 5, TotalSize, uint64_t)
34+
MIBEntryDef(MinSize = 6, MinSize, uint32_t)
35+
MIBEntryDef(MaxSize = 7, MaxSize, uint32_t)
36+
MIBEntryDef(AllocTimestamp = 8, AllocTimestamp, uint32_t)
37+
MIBEntryDef(DeallocTimestamp = 9, DeallocTimestamp, uint32_t)
38+
MIBEntryDef(TotalLifetime = 10, TotalLifetime, uint64_t)
39+
MIBEntryDef(MinLifetime = 11, MinLifetime, uint32_t)
40+
MIBEntryDef(MaxLifetime = 12, MaxLifetime, uint32_t)
41+
MIBEntryDef(AllocCpuId = 13, AllocCpuId, uint32_t)
42+
MIBEntryDef(DeallocCpuId = 14, DeallocCpuId, uint32_t)
43+
MIBEntryDef(NumMigratedCpu = 15, NumMigratedCpu, uint32_t)
44+
MIBEntryDef(NumLifetimeOverlaps = 16, NumLifetimeOverlaps, uint32_t)
45+
MIBEntryDef(NumSameAllocCpu = 17, NumSameAllocCpu, uint32_t)
46+
MIBEntryDef(NumSameDeallocCpu = 18, NumSameDeallocCpu, uint32_t)
47+
MIBEntryDef(DataTypeId = 19, DataTypeId, uint64_t)
48+
MIBEntryDef(TotalAccessDensity = 20, TotalAccessDensity, uint64_t)
49+
MIBEntryDef(MinAccessDensity = 21, MinAccessDensity, uint32_t)
50+
MIBEntryDef(MaxAccessDensity = 22, MaxAccessDensity, uint32_t)
51+
MIBEntryDef(TotalLifetimeAccessDensity = 23, TotalLifetimeAccessDensity, uint64_t)
52+
MIBEntryDef(MinLifetimeAccessDensity = 24, MinLifetimeAccessDensity, uint32_t)
53+
MIBEntryDef(MaxLifetimeAccessDensity = 25, MaxLifetimeAccessDensity, uint32_t)
54+
MIBEntryDef(AccessHistogramSize = 26, AccessHistogramSize, uint32_t)
55+
MIBEntryDef(AccessHistogram = 27, AccessHistogram, uintptr_t)
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
#ifndef MEMPROF_DATA_INC
2+
#define MEMPROF_DATA_INC
3+
/*===-- MemProfData.inc - MemProf profiling runtime structures -*- C++ -*-=== *\
4+
|*
5+
|* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
6+
|* See https://llvm.org/LICENSE.txt for license information.
7+
|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8+
|*
9+
\*===----------------------------------------------------------------------===*/
10+
/*
11+
* This is the main file that defines all the data structure, signature,
12+
* constant literals that are shared across profiling runtime library,
13+
* and host tools (reader/writer).
14+
*
15+
* This file has two identical copies. The primary copy lives in LLVM and
16+
* the other one sits in compiler-rt/include/profile directory. To make changes
17+
* in this file, first modify the primary copy and copy it over to compiler-rt.
18+
* Testing of any change in this file can start only after the two copies are
19+
* synced up.
20+
*
21+
\*===----------------------------------------------------------------------===*/
22+
#include <string.h>
23+
24+
#ifdef _MSC_VER
25+
#define PACKED(...) __pragma(pack(push,1)) __VA_ARGS__ __pragma(pack(pop))
26+
#else
27+
#define PACKED(...) __VA_ARGS__ __attribute__((__packed__))
28+
#endif
29+
30+
// A 64-bit magic number to uniquely identify the raw binary memprof profile file.
31+
#define MEMPROF_RAW_MAGIC_64 \
32+
((uint64_t)255 << 56 | (uint64_t)'m' << 48 | (uint64_t)'p' << 40 | (uint64_t)'r' << 32 | \
33+
(uint64_t)'o' << 24 | (uint64_t)'f' << 16 | (uint64_t)'r' << 8 | (uint64_t)129)
34+
35+
// The version number of the raw binary format.
36+
#define MEMPROF_RAW_VERSION 4ULL
37+
38+
// Currently supported versions.
39+
#define MEMPROF_RAW_SUPPORTED_VERSIONS \
40+
{ 3ULL, 4ULL }
41+
42+
#define MEMPROF_V3_MIB_SIZE 132ULL;
43+
44+
#define MEMPROF_BUILDID_MAX_SIZE 32ULL
45+
46+
namespace llvm {
47+
namespace memprof {
48+
// A struct describing the header used for the raw binary memprof profile format.
49+
PACKED(struct Header {
50+
uint64_t Magic;
51+
uint64_t Version;
52+
uint64_t TotalSize;
53+
uint64_t SegmentOffset;
54+
uint64_t MIBOffset;
55+
uint64_t StackOffset;
56+
});
57+
58+
// A struct describing the information necessary to describe a /proc/maps
59+
// segment entry for a particular binary/library identified by its build id.
60+
PACKED(struct SegmentEntry {
61+
uint64_t Start;
62+
uint64_t End;
63+
uint64_t Offset;
64+
uint64_t BuildIdSize;
65+
uint8_t BuildId[MEMPROF_BUILDID_MAX_SIZE] = {0};
66+
67+
// This constructor is only used in tests so don't set the BuildId.
68+
SegmentEntry(uint64_t S, uint64_t E, uint64_t O)
69+
: Start(S), End(E), Offset(O), BuildIdSize(0) {}
70+
71+
SegmentEntry(const SegmentEntry& S) {
72+
Start = S.Start;
73+
End = S.End;
74+
Offset = S.Offset;
75+
BuildIdSize = S.BuildIdSize;
76+
memcpy(BuildId, S.BuildId, S.BuildIdSize);
77+
}
78+
79+
SegmentEntry& operator=(const SegmentEntry& S) {
80+
Start = S.Start;
81+
End = S.End;
82+
Offset = S.Offset;
83+
BuildIdSize = S.BuildIdSize;
84+
memcpy(BuildId, S.BuildId, S.BuildIdSize);
85+
return *this;
86+
}
87+
88+
bool operator==(const SegmentEntry& S) const {
89+
return Start == S.Start && End == S.End && Offset == S.Offset &&
90+
BuildIdSize == S.BuildIdSize &&
91+
memcmp(BuildId, S.BuildId, S.BuildIdSize) == 0;
92+
}
93+
});
94+
95+
// Packed struct definition for MSVC. We can't use the PACKED macro defined in
96+
// MemProfData.inc since it would mean we are embedding a directive (the
97+
// #include for MIBEntryDef) into the macros which is undefined behaviour.
98+
#ifdef _MSC_VER
99+
__pragma(pack(push,1))
100+
#endif
101+
102+
// A struct representing the heap allocation characteristics of a particular
103+
// runtime context. This struct is shared between the compiler-rt runtime and
104+
// the raw profile reader. The indexed format uses a separate, self-describing
105+
// backwards compatible format.
106+
struct MemInfoBlock{
107+
108+
#define MIBEntryDef(NameTag, Name, Type) Type Name;
109+
#include "MIBEntryDef.inc"
110+
#undef MIBEntryDef
111+
112+
bool operator==(const MemInfoBlock& Other) const {
113+
bool IsEqual = true;
114+
#define MIBEntryDef(NameTag, Name, Type) \
115+
IsEqual = (IsEqual && Name == Other.Name);
116+
#include "MIBEntryDef.inc"
117+
#undef MIBEntryDef
118+
return IsEqual;
119+
}
120+
121+
MemInfoBlock() {
122+
#define MIBEntryDef(NameTag, Name, Type) Name = Type();
123+
#include "MIBEntryDef.inc"
124+
#undef MIBEntryDef
125+
}
126+
127+
MemInfoBlock(uint32_t Size, uint64_t AccessCount, uint32_t AllocTs,
128+
uint32_t DeallocTs, uint32_t AllocCpu, uint32_t DeallocCpu,
129+
uintptr_t Histogram, uint32_t HistogramSize)
130+
: MemInfoBlock() {
131+
AllocCount = 1U;
132+
TotalAccessCount = AccessCount;
133+
MinAccessCount = AccessCount;
134+
MaxAccessCount = AccessCount;
135+
TotalSize = Size;
136+
MinSize = Size;
137+
MaxSize = Size;
138+
AllocTimestamp = AllocTs;
139+
DeallocTimestamp = DeallocTs;
140+
TotalLifetime = DeallocTimestamp - AllocTimestamp;
141+
MinLifetime = TotalLifetime;
142+
MaxLifetime = TotalLifetime;
143+
// Access density is accesses per byte. Multiply by 100 to include the
144+
// fractional part.
145+
TotalAccessDensity = AccessCount * 100 / Size;
146+
MinAccessDensity = TotalAccessDensity;
147+
MaxAccessDensity = TotalAccessDensity;
148+
// Lifetime access density is the access density per second of lifetime.
149+
// Multiply by 1000 to convert denominator lifetime to seconds (using a
150+
// minimum lifetime of 1ms to avoid divide by 0. Do the multiplication first
151+
// to reduce truncations to 0.
152+
TotalLifetimeAccessDensity =
153+
TotalAccessDensity * 1000 / (TotalLifetime ? TotalLifetime : 1);
154+
MinLifetimeAccessDensity = TotalLifetimeAccessDensity;
155+
MaxLifetimeAccessDensity = TotalLifetimeAccessDensity;
156+
AllocCpuId = AllocCpu;
157+
DeallocCpuId = DeallocCpu;
158+
NumMigratedCpu = AllocCpuId != DeallocCpuId;
159+
AccessHistogramSize = HistogramSize;
160+
AccessHistogram = Histogram;
161+
}
162+
163+
void Merge(const MemInfoBlock &newMIB) {
164+
AllocCount += newMIB.AllocCount;
165+
166+
TotalAccessCount += newMIB.TotalAccessCount;
167+
MinAccessCount = newMIB.MinAccessCount < MinAccessCount ? newMIB.MinAccessCount : MinAccessCount;
168+
MaxAccessCount = newMIB.MaxAccessCount > MaxAccessCount ? newMIB.MaxAccessCount : MaxAccessCount;
169+
170+
TotalSize += newMIB.TotalSize;
171+
MinSize = newMIB.MinSize < MinSize ? newMIB.MinSize : MinSize;
172+
MaxSize = newMIB.MaxSize > MaxSize ? newMIB.MaxSize : MaxSize;
173+
174+
TotalLifetime += newMIB.TotalLifetime;
175+
MinLifetime = newMIB.MinLifetime < MinLifetime ? newMIB.MinLifetime : MinLifetime;
176+
MaxLifetime = newMIB.MaxLifetime > MaxLifetime ? newMIB.MaxLifetime : MaxLifetime;
177+
178+
TotalAccessDensity += newMIB.TotalAccessDensity;
179+
MinAccessDensity = newMIB.MinAccessDensity < MinAccessDensity
180+
? newMIB.MinAccessDensity
181+
: MinAccessDensity;
182+
MaxAccessDensity = newMIB.MaxAccessDensity > MaxAccessDensity
183+
? newMIB.MaxAccessDensity
184+
: MaxAccessDensity;
185+
186+
TotalLifetimeAccessDensity += newMIB.TotalLifetimeAccessDensity;
187+
MinLifetimeAccessDensity =
188+
newMIB.MinLifetimeAccessDensity < MinLifetimeAccessDensity
189+
? newMIB.MinLifetimeAccessDensity
190+
: MinLifetimeAccessDensity;
191+
MaxLifetimeAccessDensity =
192+
newMIB.MaxLifetimeAccessDensity > MaxLifetimeAccessDensity
193+
? newMIB.MaxLifetimeAccessDensity
194+
: MaxLifetimeAccessDensity;
195+
196+
// We know newMIB was deallocated later, so just need to check if it was
197+
// allocated before last one deallocated.
198+
NumLifetimeOverlaps += newMIB.AllocTimestamp < DeallocTimestamp;
199+
AllocTimestamp = newMIB.AllocTimestamp;
200+
DeallocTimestamp = newMIB.DeallocTimestamp;
201+
202+
NumSameAllocCpu += AllocCpuId == newMIB.AllocCpuId;
203+
NumSameDeallocCpu += DeallocCpuId == newMIB.DeallocCpuId;
204+
AllocCpuId = newMIB.AllocCpuId;
205+
DeallocCpuId = newMIB.DeallocCpuId;
206+
207+
// For merging histograms, we always keep the longer histogram, and add
208+
// values of shorter histogram to larger one.
209+
uintptr_t ShorterHistogram;
210+
uint32_t ShorterHistogramSize;
211+
if (newMIB.AccessHistogramSize > AccessHistogramSize) {
212+
ShorterHistogram = AccessHistogram;
213+
ShorterHistogramSize = AccessHistogramSize;
214+
// Swap histogram of current to larger histogram
215+
AccessHistogram = newMIB.AccessHistogram;
216+
AccessHistogramSize = newMIB.AccessHistogramSize;
217+
} else {
218+
ShorterHistogram = newMIB.AccessHistogram;
219+
ShorterHistogramSize = newMIB.AccessHistogramSize;
220+
}
221+
for (size_t i = 0; i < ShorterHistogramSize; ++i) {
222+
((uint64_t *)AccessHistogram)[i] += ((uint64_t *)ShorterHistogram)[i];
223+
}
224+
}
225+
226+
#ifdef _MSC_VER
227+
} __pragma(pack(pop));
228+
#else
229+
} __attribute__((__packed__));
230+
#endif
231+
232+
} // namespace memprof
233+
} // namespace llvm
234+
235+
#endif
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*===---- instr_prof_interface.h - Instrumentation PGO User Program API ----===
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+
*
9+
* This header provides a public interface for fine-grained control of counter
10+
* reset and profile dumping. These interface functions can be directly called
11+
* in user programs.
12+
*
13+
\*===---------------------------------------------------------------------===*/
14+
15+
#ifndef COMPILER_RT_INSTR_PROFILING
16+
#define COMPILER_RT_INSTR_PROFILING
17+
18+
#ifdef __cplusplus
19+
extern "C" {
20+
#endif
21+
22+
#ifdef __LLVM_INSTR_PROFILE_GENERATE
23+
// Profile file reset and dump interfaces.
24+
// When `-fprofile[-instr]-generate`/`-fcs-profile-generate` is in effect,
25+
// clang defines __LLVM_INSTR_PROFILE_GENERATE to pick up the API calls.
26+
27+
/*!
28+
* \brief Set the filename for writing instrumentation data.
29+
*
30+
* Sets the filename to be used for subsequent calls to
31+
* \a __llvm_profile_write_file().
32+
*
33+
* \c Name is not copied, so it must remain valid. Passing NULL resets the
34+
* filename logic to the default behaviour.
35+
*
36+
* Note: There may be multiple copies of the profile runtime (one for each
37+
* instrumented image/DSO). This API only modifies the filename within the
38+
* copy of the runtime available to the calling image.
39+
*
40+
* Warning: This is a no-op if continuous mode (\ref
41+
* __llvm_profile_is_continuous_mode_enabled) is on. The reason for this is
42+
* that in continuous mode, profile counters are mmap()'d to the profile at
43+
* program initialization time. Support for transferring the mmap'd profile
44+
* counts to a new file has not been implemented.
45+
*/
46+
void __llvm_profile_set_filename(const char *Name);
47+
48+
/*!
49+
* \brief Interface to set all PGO counters to zero for the current process.
50+
*
51+
*/
52+
void __llvm_profile_reset_counters(void);
53+
54+
/*!
55+
* \brief this is a wrapper interface to \c __llvm_profile_write_file.
56+
* After this interface is invoked, an already dumped flag will be set
57+
* so that profile won't be dumped again during program exit.
58+
* Invocation of interface __llvm_profile_reset_counters will clear
59+
* the flag. This interface is designed to be used to collect profile
60+
* data from user selected hot regions. The use model is
61+
* __llvm_profile_reset_counters();
62+
* ... hot region 1
63+
* __llvm_profile_dump();
64+
* .. some other code
65+
* __llvm_profile_reset_counters();
66+
* ... hot region 2
67+
* __llvm_profile_dump();
68+
*
69+
* It is expected that on-line profile merging is on with \c %m specifier
70+
* used in profile filename . If merging is not turned on, user is expected
71+
* to invoke __llvm_profile_set_filename to specify different profile names
72+
* for different regions before dumping to avoid profile write clobbering.
73+
*/
74+
int __llvm_profile_dump(void);
75+
76+
// Interface to dump the current process' order file to disk.
77+
int __llvm_orderfile_dump(void);
78+
79+
#else
80+
81+
#define __llvm_profile_set_filename(Name)
82+
#define __llvm_profile_reset_counters()
83+
#define __llvm_profile_dump() (0)
84+
#define __llvm_orderfile_dump() (0)
85+
86+
#endif
87+
88+
#ifdef __cplusplus
89+
} // extern "C"
90+
#endif
91+
92+
#endif

0 commit comments

Comments
 (0)