[compiler-rt] Add baremetal version of profile library. (#167998)

Adds a flag COMPILER_RT_PROFILE_BAREMETAL, which disables the parts of
the profile runtime which require a filesystem or malloc. This minimal
library only requires string.h from the C library.

This is useful for profiling or code coverage of baremetal images, which
don't have filesystem APIs, and might not have malloc configured (or
have limited heap space).

Expected usage:

- Add code to your project to call
`__llvm_profile_get_size_for_buffer()` and
`__llvm_profile_write_buffer()` to write the profile data to a buffer in
memory, and then copy that data off the device using target-specific
tools.
- If you're using a linker script, set up your linker script to map the
profiling and coverage input sections to corresponding output sections
with the same name, and mark them KEEP. `__llvm_covfun` and
`__llvm_covmap` are non-allocatable, `__llvm_prf_names` is read-only
allocatable, and `__llvm_prf_cnts` and `__llvm_prf_data` are read-write
allocatable.
- The resulting data is in same format as the non-baremetal profiles.

There's some room for improvement here in the future for doing profiling
and code coverage for baremetal. If we revised the profiling format, and
introduced some additional host tooling, we could move some of the
metadata into non-allocated sections, and construct the profraw file on
the host. But this patch is sufficient for some use-cases.
This commit is contained in:
Eli Friedman
2025-12-10 12:58:25 -08:00
committed by GitHub
parent 04dcb1b9ee
commit bc0d0bbc6b
10 changed files with 70 additions and 24 deletions

View File

@@ -312,6 +312,8 @@ option(COMPILER_RT_USE_BUILTINS_LIBRARY
option(COMPILER_RT_USE_ATOMIC_LIBRARY "Use compiler-rt atomic instead of libatomic" OFF)
option(COMPILER_RT_PROFILE_BAREMETAL "Build minimal baremetal profile library" OFF)
include(config-ix)
#================================

View File

@@ -842,7 +842,7 @@ else()
endif()
if (PROFILE_SUPPORTED_ARCH AND NOT LLVM_USE_SANITIZER AND
OS_NAME MATCHES "Darwin|Linux|FreeBSD|Windows|Android|Fuchsia|SunOS|NetBSD|AIX|WASI|Haiku")
(OS_NAME MATCHES "Darwin|Linux|FreeBSD|Windows|Android|Fuchsia|SunOS|NetBSD|AIX|WASI|Haiku" OR COMPILER_RT_PROFILE_BAREMETAL))
set(COMPILER_RT_HAS_PROFILE TRUE)
else()
set(COMPILER_RT_HAS_PROFILE FALSE)

View File

@@ -60,12 +60,9 @@ int main() {
add_compiler_rt_component(profile)
set(PROFILE_SOURCES
GCDAProfiling.c
InstrProfiling.c
InstrProfilingInternal.c
InstrProfilingValue.c
InstrProfilingBuffer.c
InstrProfilingFile.c
InstrProfilingMerge.c
InstrProfilingMergeFile.c
InstrProfilingNameVar.c
@@ -77,10 +74,26 @@ set(PROFILE_SOURCES
InstrProfilingPlatformLinux.c
InstrProfilingPlatformOther.c
InstrProfilingPlatformWindows.c
InstrProfilingRuntime.cpp
InstrProfilingUtil.c
)
if (NOT COMPILER_RT_PROFILE_BAREMETAL)
# For baremetal, exclude the following:
# - Anything that contains filesystem operations (InstrProfilingFile.c,
# InstrProfilingUtils.c)
# - Initialization, because it isn't necesary without the filesystem bits
# on ELF targets (InstrProfilingRuntime.cpp).
# - Value profiling, because it requires malloc (InstrProfilingValue.c).
# This could be optional if someone needs it.
# - GCDA profiling, which is unrelated (GCDAProfiling.c)
list(APPEND PROFILE_SOURCES
GCDAProfiling.c
InstrProfilingFile.c
InstrProfilingRuntime.cpp
InstrProfilingUtil.c
InstrProfilingValue.c
)
endif()
set(PROFILE_HEADERS
InstrProfiling.h
InstrProfilingInternal.h
@@ -135,6 +148,12 @@ if(COMPILER_RT_TARGET_HAS_UNAME)
-DCOMPILER_RT_HAS_UNAME=1)
endif()
if(COMPILER_RT_PROFILE_BAREMETAL)
set(EXTRA_FLAGS
${EXTRA_FLAGS}
-DCOMPILER_RT_PROFILE_BAREMETAL=1)
endif()
if(MSVC)
# profile historically has only been supported with the static runtime
# on windows

View File

@@ -10,8 +10,6 @@
// with freestanding compilation. See `darwin_add_builtin_libraries`.
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "InstrProfiling.h"

View File

@@ -10,7 +10,10 @@
#define PROFILE_INSTRPROFILING_H_
#include "InstrProfilingPort.h"
#include <stddef.h>
#ifndef COMPILER_RT_PROFILE_BAREMETAL
#include <stdio.h>
#endif
// Make sure __LLVM_INSTR_PROFILE_GENERATE is always defined before
// including instr_prof_interface.h so the interface functions are
@@ -200,7 +203,9 @@ int __llvm_profile_write_file(void);
* copying the old profile file to new profile file and this function is usually
* used when the proess doesn't have permission to open file.
*/
#ifndef COMPILER_RT_PROFILE_BAREMETAL
int __llvm_profile_set_file_object(FILE *File, int EnableMerge);
#endif
/*! \brief Register to write instrumentation data to file at exit. */
int __llvm_profile_register_write_file_atexit(void);

View File

@@ -11,7 +11,6 @@
#include "InstrProfiling.h"
#include "InstrProfilingInternal.h"
#include "InstrProfilingUtil.h"
#define INSTR_PROF_VALUE_PROF_DATA
#include "profile/InstrProfData.inc"

View File

@@ -13,7 +13,6 @@
#include "InstrProfiling.h"
#include "InstrProfilingInternal.h"
#include "InstrProfilingUtil.h"
#define INSTR_PROF_VALUE_PROF_DATA
#include "profile/InstrProfData.inc"

View File

@@ -6,16 +6,33 @@
|*
\*===----------------------------------------------------------------------===*/
// This file defines profile data symbols for ELF, wasm, XCOFF. It assumes
// __start_ and __stop_ symbols for profile data point at the beginning and
// end of the sections in question. (This is technically a linker feature,
// not a file format feature, but linkers for these targets support it.)
//
// MachO (MacOS/iOS) and PE-COFF (Windows) have a similar support, but the
// identifiers are different, so the support is in separate files.
//
// Support for targets which don't have linker support is in
// InstrProfilingPlatformOther.c.
//
// This file also contains code to extract ELF build IDs from the ELF file,
// to identify the build which generated the file.
#if defined(__linux__) || defined(__FreeBSD__) || defined(__Fuchsia__) || \
(defined(__sun__) && defined(__svr4__)) || defined(__NetBSD__) || \
defined(_AIX) || defined(__wasm__) || defined(__HAIKU__)
defined(_AIX) || defined(__wasm__) || defined(__HAIKU__) || \
defined(COMPILER_RT_PROFILE_BAREMETAL)
#if !defined(_AIX) && !defined(__wasm__)
#if !defined(_AIX) && !defined(__wasm__) && \
!defined(COMPILER_RT_PROFILE_BAREMETAL)
// Includes for non-baremetal ELF targets, used to output build IDs.
#include <elf.h>
#include <link.h>
#endif
#include <stdlib.h>
#include <string.h>
#endif
#include "InstrProfiling.h"
#include "InstrProfilingInternal.h"

View File

@@ -6,10 +6,18 @@
|*
\*===----------------------------------------------------------------------===*/
// This file defines a fallback implementation to compute the locations of
// profile data sections, for targets that don't have linker support. No
// commonly used targets use this codepath.
//
// This implementation expects the compiler instrumentation pass to define a
// constructor in each file which calls into this file.
#if !defined(__APPLE__) && !defined(__linux__) && !defined(__FreeBSD__) && \
!defined(__Fuchsia__) && !(defined(__sun__) && defined(__svr4__)) && \
!defined(__NetBSD__) && !defined(_WIN32) && !defined(_AIX) && \
!defined(__wasm__) && !defined(__HAIKU__)
!defined(__wasm__) && !defined(__HAIKU__) && \
!defined(COMPILER_RT_PROFILE_BAREMETAL)
#include <stdlib.h>
#include <stdio.h>

View File

@@ -117,9 +117,17 @@ static inline size_t getpagesize(void) {
return S.dwPageSize;
}
#else /* defined(_WIN32) */
#ifndef COMPILER_RT_PROFILE_BAREMETAL
#include <unistd.h>
#endif
#endif /* defined(_WIN32) */
#ifdef COMPILER_RT_PROFILE_BAREMETAL
// Baremetal doesn't support logging
#define PROF_ERR(Format, ...)
#define PROF_WARN(Format, ...)
#define PROF_NOTE(Format, ...)
#else
#define PROF_ERR(Format, ...) \
fprintf(stderr, "LLVM Profile Error: " Format, __VA_ARGS__);
@@ -128,6 +136,7 @@ static inline size_t getpagesize(void) {
#define PROF_NOTE(Format, ...) \
fprintf(stderr, "LLVM Profile Note: " Format, __VA_ARGS__);
#endif /* COMPILER_RT_PROFILE_BAREMETAL */
#ifndef MAP_FILE
#define MAP_FILE 0
@@ -137,16 +146,6 @@ static inline size_t getpagesize(void) {
#define O_BINARY 0
#endif
#if defined(__FreeBSD__)
#include <inttypes.h>
#include <sys/types.h>
#else /* defined(__FreeBSD__) */
#include <inttypes.h>
#include <stdint.h>
#endif /* defined(__FreeBSD__) && defined(__i386__) */
#endif /* PROFILE_INSTRPROFILING_PORT_H_ */