mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-04 09:00:33 +03:00
LLVM7 (#2490)
* MCAsmBackend changes * Add profile-rt-70 * ProfData definition * ProfData tool * Remove profile-rt CMakeLists.txt * Update druntime submodule
This commit is contained in:
parent
6b462b1018
commit
a73188c8e5
25 changed files with 5039 additions and 2 deletions
|
@ -1 +1 @@
|
|||
Subproject commit 427edc0bf70b737187dd205c4c114234387e1b88
|
||||
Subproject commit d011f559ad6d8494727890ab0a902e68cbc13421
|
|
@ -266,7 +266,12 @@ void disassemble(const llvm::TargetMachine &tm,
|
|||
|
||||
llvm::MCTargetOptions opts;
|
||||
auto mab = unique(target.createMCAsmBackend(
|
||||
*mri, tm.getTargetTriple().getTriple(), tm.getTargetCPU(), opts));
|
||||
#if LDC_LLVM_VER >= 700
|
||||
*sti,*mri,opts)
|
||||
#else
|
||||
*mri, tm.getTargetTriple().getTriple(), tm.getTargetCPU(), opts)
|
||||
#endif
|
||||
);
|
||||
if (nullptr == mab) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -84,6 +84,16 @@ extern(C++) struct ProfileData {
|
|||
uint NumCounters;
|
||||
ushort NumValueSites;
|
||||
}
|
||||
else version(LDC_LLVM_700)
|
||||
{
|
||||
ulong NameRef;
|
||||
ulong FuncHash;
|
||||
ulong* Counter;
|
||||
void* FunctionPointer;
|
||||
void* Values;
|
||||
uint NumCounters;
|
||||
ushort NumValueSites;
|
||||
}
|
||||
else
|
||||
{
|
||||
static assert(0, "unsupported LLVM version");
|
||||
|
|
558
runtime/profile-rt/profile-rt-70/GCDAProfiling.c
Normal file
558
runtime/profile-rt/profile-rt-70/GCDAProfiling.c
Normal file
|
@ -0,0 +1,558 @@
|
|||
/*===- GCDAProfiling.c - Support library for GCDA file emission -----------===*\
|
||||
|*
|
||||
|* The LLVM Compiler Infrastructure
|
||||
|*
|
||||
|* This file is distributed under the University of Illinois Open Source
|
||||
|* License. See LICENSE.TXT for details.
|
||||
|*
|
||||
|*===----------------------------------------------------------------------===*|
|
||||
|*
|
||||
|* This file implements the call back routines for the gcov profiling
|
||||
|* instrumentation pass. Link against this library when running code through
|
||||
|* the -insert-gcov-profiling LLVM pass.
|
||||
|*
|
||||
|* We emit files in a corrupt version of GCOV's "gcda" file format. These files
|
||||
|* are only close enough that LCOV will happily parse them. Anything that lcov
|
||||
|* ignores is missing.
|
||||
|*
|
||||
|* TODO: gcov is multi-process safe by having each exit open the existing file
|
||||
|* and append to it. We'd like to achieve that and be thread-safe too.
|
||||
|*
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
#include "InstrProfilingPort.h"
|
||||
#include "InstrProfilingUtil.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include "WindowsMMap.h"
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#include <sys/file.h>
|
||||
#ifndef MAP_FILE
|
||||
#define MAP_FILE 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__FreeBSD__) && defined(__i386__)
|
||||
#define I386_FREEBSD 1
|
||||
#else
|
||||
#define I386_FREEBSD 0
|
||||
#endif
|
||||
|
||||
#if !defined(_MSC_VER) && !I386_FREEBSD
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
#elif I386_FREEBSD
|
||||
/* System headers define 'size_t' incorrectly on x64 FreeBSD (prior to
|
||||
* FreeBSD 10, r232261) when compiled in 32-bit mode.
|
||||
*/
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
#endif
|
||||
|
||||
/* #define DEBUG_GCDAPROFILING */
|
||||
|
||||
/*
|
||||
* --- GCOV file format I/O primitives ---
|
||||
*/
|
||||
|
||||
/*
|
||||
* The current file name we're outputting. Used primarily for error logging.
|
||||
*/
|
||||
static char *filename = NULL;
|
||||
|
||||
/*
|
||||
* The current file we're outputting.
|
||||
*/
|
||||
static FILE *output_file = NULL;
|
||||
|
||||
/*
|
||||
* Buffer that we write things into.
|
||||
*/
|
||||
#define WRITE_BUFFER_SIZE (128 * 1024)
|
||||
static char *write_buffer = NULL;
|
||||
static uint64_t cur_buffer_size = 0;
|
||||
static uint64_t cur_pos = 0;
|
||||
static uint64_t file_size = 0;
|
||||
static int new_file = 0;
|
||||
static int fd = -1;
|
||||
|
||||
/*
|
||||
* A list of functions to write out the data.
|
||||
*/
|
||||
typedef void (*writeout_fn)();
|
||||
|
||||
struct writeout_fn_node {
|
||||
writeout_fn fn;
|
||||
struct writeout_fn_node *next;
|
||||
};
|
||||
|
||||
static struct writeout_fn_node *writeout_fn_head = NULL;
|
||||
static struct writeout_fn_node *writeout_fn_tail = NULL;
|
||||
|
||||
/*
|
||||
* A list of flush functions that our __gcov_flush() function should call.
|
||||
*/
|
||||
typedef void (*flush_fn)();
|
||||
|
||||
struct flush_fn_node {
|
||||
flush_fn fn;
|
||||
struct flush_fn_node *next;
|
||||
};
|
||||
|
||||
static struct flush_fn_node *flush_fn_head = NULL;
|
||||
static struct flush_fn_node *flush_fn_tail = NULL;
|
||||
|
||||
static void resize_write_buffer(uint64_t size) {
|
||||
if (!new_file) return;
|
||||
size += cur_pos;
|
||||
if (size <= cur_buffer_size) return;
|
||||
size = (size - 1) / WRITE_BUFFER_SIZE + 1;
|
||||
size *= WRITE_BUFFER_SIZE;
|
||||
write_buffer = realloc(write_buffer, size);
|
||||
cur_buffer_size = size;
|
||||
}
|
||||
|
||||
static void write_bytes(const char *s, size_t len) {
|
||||
resize_write_buffer(len);
|
||||
memcpy(&write_buffer[cur_pos], s, len);
|
||||
cur_pos += len;
|
||||
}
|
||||
|
||||
static void write_32bit_value(uint32_t i) {
|
||||
write_bytes((char*)&i, 4);
|
||||
}
|
||||
|
||||
static void write_64bit_value(uint64_t i) {
|
||||
write_bytes((char*)&i, 8);
|
||||
}
|
||||
|
||||
static uint32_t length_of_string(const char *s) {
|
||||
return (strlen(s) / 4) + 1;
|
||||
}
|
||||
|
||||
static void write_string(const char *s) {
|
||||
uint32_t len = length_of_string(s);
|
||||
write_32bit_value(len);
|
||||
write_bytes(s, strlen(s));
|
||||
write_bytes("\0\0\0\0", 4 - (strlen(s) % 4));
|
||||
}
|
||||
|
||||
static uint32_t read_32bit_value() {
|
||||
uint32_t val;
|
||||
|
||||
if (new_file)
|
||||
return (uint32_t)-1;
|
||||
|
||||
val = *(uint32_t*)&write_buffer[cur_pos];
|
||||
cur_pos += 4;
|
||||
return val;
|
||||
}
|
||||
|
||||
static uint64_t read_64bit_value() {
|
||||
uint64_t val;
|
||||
|
||||
if (new_file)
|
||||
return (uint64_t)-1;
|
||||
|
||||
val = *(uint64_t*)&write_buffer[cur_pos];
|
||||
cur_pos += 8;
|
||||
return val;
|
||||
}
|
||||
|
||||
static char *mangle_filename(const char *orig_filename) {
|
||||
char *new_filename;
|
||||
size_t prefix_len;
|
||||
int prefix_strip;
|
||||
const char *prefix = lprofGetPathPrefix(&prefix_strip, &prefix_len);
|
||||
|
||||
if (prefix == NULL)
|
||||
return strdup(orig_filename);
|
||||
|
||||
new_filename = malloc(prefix_len + 1 + strlen(orig_filename) + 1);
|
||||
lprofApplyPathPrefix(new_filename, orig_filename, prefix, prefix_len,
|
||||
prefix_strip);
|
||||
|
||||
return new_filename;
|
||||
}
|
||||
|
||||
static int map_file() {
|
||||
fseek(output_file, 0L, SEEK_END);
|
||||
file_size = ftell(output_file);
|
||||
|
||||
/* A size of 0 is invalid to `mmap'. Return a fail here, but don't issue an
|
||||
* error message because it should "just work" for the user. */
|
||||
if (file_size == 0)
|
||||
return -1;
|
||||
|
||||
write_buffer = mmap(0, file_size, PROT_READ | PROT_WRITE,
|
||||
MAP_FILE | MAP_SHARED, fd, 0);
|
||||
if (write_buffer == (void *)-1) {
|
||||
int errnum = errno;
|
||||
fprintf(stderr, "profiling: %s: cannot map: %s\n", filename,
|
||||
strerror(errnum));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void unmap_file() {
|
||||
if (msync(write_buffer, file_size, MS_SYNC) == -1) {
|
||||
int errnum = errno;
|
||||
fprintf(stderr, "profiling: %s: cannot msync: %s\n", filename,
|
||||
strerror(errnum));
|
||||
}
|
||||
|
||||
/* We explicitly ignore errors from unmapping because at this point the data
|
||||
* is written and we don't care.
|
||||
*/
|
||||
(void)munmap(write_buffer, file_size);
|
||||
write_buffer = NULL;
|
||||
file_size = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* --- LLVM line counter API ---
|
||||
*/
|
||||
|
||||
/* A file in this case is a translation unit. Each .o file built with line
|
||||
* profiling enabled will emit to a different file. Only one file may be
|
||||
* started at a time.
|
||||
*/
|
||||
void llvm_gcda_start_file(const char *orig_filename, const char version[4],
|
||||
uint32_t checksum) {
|
||||
const char *mode = "r+b";
|
||||
filename = mangle_filename(orig_filename);
|
||||
|
||||
/* Try just opening the file. */
|
||||
new_file = 0;
|
||||
fd = open(filename, O_RDWR);
|
||||
|
||||
if (fd == -1) {
|
||||
/* Try opening the file, creating it if necessary. */
|
||||
new_file = 1;
|
||||
mode = "w+b";
|
||||
fd = open(filename, O_RDWR | O_CREAT, 0644);
|
||||
if (fd == -1) {
|
||||
/* Try creating the directories first then opening the file. */
|
||||
__llvm_profile_recursive_mkdir(filename);
|
||||
fd = open(filename, O_RDWR | O_CREAT, 0644);
|
||||
if (fd == -1) {
|
||||
/* Bah! It's hopeless. */
|
||||
int errnum = errno;
|
||||
fprintf(stderr, "profiling: %s: cannot open: %s\n", filename,
|
||||
strerror(errnum));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to flock the file to serialize concurrent processes writing out to the
|
||||
* same GCDA. This can fail if the filesystem doesn't support it, but in that
|
||||
* case we'll just carry on with the old racy behaviour and hope for the best.
|
||||
*/
|
||||
flock(fd, LOCK_EX);
|
||||
output_file = fdopen(fd, mode);
|
||||
|
||||
/* Initialize the write buffer. */
|
||||
write_buffer = NULL;
|
||||
cur_buffer_size = 0;
|
||||
cur_pos = 0;
|
||||
|
||||
if (new_file) {
|
||||
resize_write_buffer(WRITE_BUFFER_SIZE);
|
||||
memset(write_buffer, 0, WRITE_BUFFER_SIZE);
|
||||
} else {
|
||||
if (map_file() == -1) {
|
||||
/* mmap failed, try to recover by clobbering */
|
||||
new_file = 1;
|
||||
write_buffer = NULL;
|
||||
cur_buffer_size = 0;
|
||||
resize_write_buffer(WRITE_BUFFER_SIZE);
|
||||
memset(write_buffer, 0, WRITE_BUFFER_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
/* gcda file, version, stamp checksum. */
|
||||
write_bytes("adcg", 4);
|
||||
write_bytes(version, 4);
|
||||
write_32bit_value(checksum);
|
||||
|
||||
#ifdef DEBUG_GCDAPROFILING
|
||||
fprintf(stderr, "llvmgcda: [%s]\n", orig_filename);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Given an array of pointers to counters (counters), increment the n-th one,
|
||||
* where we're also given a pointer to n (predecessor).
|
||||
*/
|
||||
void llvm_gcda_increment_indirect_counter(uint32_t *predecessor,
|
||||
uint64_t **counters) {
|
||||
uint64_t *counter;
|
||||
uint32_t pred;
|
||||
|
||||
pred = *predecessor;
|
||||
if (pred == 0xffffffff)
|
||||
return;
|
||||
counter = counters[pred];
|
||||
|
||||
/* Don't crash if the pred# is out of sync. This can happen due to threads,
|
||||
or because of a TODO in GCOVProfiling.cpp buildEdgeLookupTable(). */
|
||||
if (counter)
|
||||
++*counter;
|
||||
#ifdef DEBUG_GCDAPROFILING
|
||||
else
|
||||
fprintf(stderr,
|
||||
"llvmgcda: increment_indirect_counter counters=%08llx, pred=%u\n",
|
||||
*counter, *predecessor);
|
||||
#endif
|
||||
}
|
||||
|
||||
void llvm_gcda_emit_function(uint32_t ident, const char *function_name,
|
||||
uint32_t func_checksum, uint8_t use_extra_checksum,
|
||||
uint32_t cfg_checksum) {
|
||||
uint32_t len = 2;
|
||||
|
||||
if (use_extra_checksum)
|
||||
len++;
|
||||
#ifdef DEBUG_GCDAPROFILING
|
||||
fprintf(stderr, "llvmgcda: function id=0x%08x name=%s\n", ident,
|
||||
function_name ? function_name : "NULL");
|
||||
#endif
|
||||
if (!output_file) return;
|
||||
|
||||
/* function tag */
|
||||
write_bytes("\0\0\0\1", 4);
|
||||
if (function_name)
|
||||
len += 1 + length_of_string(function_name);
|
||||
write_32bit_value(len);
|
||||
write_32bit_value(ident);
|
||||
write_32bit_value(func_checksum);
|
||||
if (use_extra_checksum)
|
||||
write_32bit_value(cfg_checksum);
|
||||
if (function_name)
|
||||
write_string(function_name);
|
||||
}
|
||||
|
||||
void llvm_gcda_emit_arcs(uint32_t num_counters, uint64_t *counters) {
|
||||
uint32_t i;
|
||||
uint64_t *old_ctrs = NULL;
|
||||
uint32_t val = 0;
|
||||
uint64_t save_cur_pos = cur_pos;
|
||||
|
||||
if (!output_file) return;
|
||||
|
||||
val = read_32bit_value();
|
||||
|
||||
if (val != (uint32_t)-1) {
|
||||
/* There are counters present in the file. Merge them. */
|
||||
if (val != 0x01a10000) {
|
||||
fprintf(stderr, "profiling: %s: cannot merge previous GCDA file: "
|
||||
"corrupt arc tag (0x%08x)\n",
|
||||
filename, val);
|
||||
return;
|
||||
}
|
||||
|
||||
val = read_32bit_value();
|
||||
if (val == (uint32_t)-1 || val / 2 != num_counters) {
|
||||
fprintf(stderr, "profiling: %s: cannot merge previous GCDA file: "
|
||||
"mismatched number of counters (%d)\n",
|
||||
filename, val);
|
||||
return;
|
||||
}
|
||||
|
||||
old_ctrs = malloc(sizeof(uint64_t) * num_counters);
|
||||
for (i = 0; i < num_counters; ++i)
|
||||
old_ctrs[i] = read_64bit_value();
|
||||
}
|
||||
|
||||
cur_pos = save_cur_pos;
|
||||
|
||||
/* Counter #1 (arcs) tag */
|
||||
write_bytes("\0\0\xa1\1", 4);
|
||||
write_32bit_value(num_counters * 2);
|
||||
for (i = 0; i < num_counters; ++i) {
|
||||
counters[i] += (old_ctrs ? old_ctrs[i] : 0);
|
||||
write_64bit_value(counters[i]);
|
||||
}
|
||||
|
||||
free(old_ctrs);
|
||||
|
||||
#ifdef DEBUG_GCDAPROFILING
|
||||
fprintf(stderr, "llvmgcda: %u arcs\n", num_counters);
|
||||
for (i = 0; i < num_counters; ++i)
|
||||
fprintf(stderr, "llvmgcda: %llu\n", (unsigned long long)counters[i]);
|
||||
#endif
|
||||
}
|
||||
|
||||
void llvm_gcda_summary_info() {
|
||||
const uint32_t obj_summary_len = 9; /* Length for gcov compatibility. */
|
||||
uint32_t i;
|
||||
uint32_t runs = 1;
|
||||
uint32_t val = 0;
|
||||
uint64_t save_cur_pos = cur_pos;
|
||||
|
||||
if (!output_file) return;
|
||||
|
||||
val = read_32bit_value();
|
||||
|
||||
if (val != (uint32_t)-1) {
|
||||
/* There are counters present in the file. Merge them. */
|
||||
if (val != 0xa1000000) {
|
||||
fprintf(stderr, "profiling: %s: cannot merge previous run count: "
|
||||
"corrupt object tag (0x%08x)\n",
|
||||
filename, val);
|
||||
return;
|
||||
}
|
||||
|
||||
val = read_32bit_value(); /* length */
|
||||
if (val != obj_summary_len) {
|
||||
fprintf(stderr, "profiling: %s: cannot merge previous run count: "
|
||||
"mismatched object length (%d)\n",
|
||||
filename, val);
|
||||
return;
|
||||
}
|
||||
|
||||
read_32bit_value(); /* checksum, unused */
|
||||
read_32bit_value(); /* num, unused */
|
||||
runs += read_32bit_value(); /* Add previous run count to new counter. */
|
||||
}
|
||||
|
||||
cur_pos = save_cur_pos;
|
||||
|
||||
/* Object summary tag */
|
||||
write_bytes("\0\0\0\xa1", 4);
|
||||
write_32bit_value(obj_summary_len);
|
||||
write_32bit_value(0); /* checksum, unused */
|
||||
write_32bit_value(0); /* num, unused */
|
||||
write_32bit_value(runs);
|
||||
for (i = 3; i < obj_summary_len; ++i)
|
||||
write_32bit_value(0);
|
||||
|
||||
/* Program summary tag */
|
||||
write_bytes("\0\0\0\xa3", 4); /* tag indicates 1 program */
|
||||
write_32bit_value(0); /* 0 length */
|
||||
|
||||
#ifdef DEBUG_GCDAPROFILING
|
||||
fprintf(stderr, "llvmgcda: %u runs\n", runs);
|
||||
#endif
|
||||
}
|
||||
|
||||
void llvm_gcda_end_file() {
|
||||
/* Write out EOF record. */
|
||||
if (output_file) {
|
||||
write_bytes("\0\0\0\0\0\0\0\0", 8);
|
||||
|
||||
if (new_file) {
|
||||
fwrite(write_buffer, cur_pos, 1, output_file);
|
||||
free(write_buffer);
|
||||
} else {
|
||||
unmap_file();
|
||||
}
|
||||
|
||||
flock(fd, LOCK_UN);
|
||||
fclose(output_file);
|
||||
output_file = NULL;
|
||||
write_buffer = NULL;
|
||||
}
|
||||
free(filename);
|
||||
|
||||
#ifdef DEBUG_GCDAPROFILING
|
||||
fprintf(stderr, "llvmgcda: -----\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void llvm_register_writeout_function(writeout_fn fn) {
|
||||
struct writeout_fn_node *new_node = malloc(sizeof(struct writeout_fn_node));
|
||||
new_node->fn = fn;
|
||||
new_node->next = NULL;
|
||||
|
||||
if (!writeout_fn_head) {
|
||||
writeout_fn_head = writeout_fn_tail = new_node;
|
||||
} else {
|
||||
writeout_fn_tail->next = new_node;
|
||||
writeout_fn_tail = new_node;
|
||||
}
|
||||
}
|
||||
|
||||
void llvm_writeout_files(void) {
|
||||
struct writeout_fn_node *curr = writeout_fn_head;
|
||||
|
||||
while (curr) {
|
||||
curr->fn();
|
||||
curr = curr->next;
|
||||
}
|
||||
}
|
||||
|
||||
void llvm_delete_writeout_function_list(void) {
|
||||
while (writeout_fn_head) {
|
||||
struct writeout_fn_node *node = writeout_fn_head;
|
||||
writeout_fn_head = writeout_fn_head->next;
|
||||
free(node);
|
||||
}
|
||||
|
||||
writeout_fn_head = writeout_fn_tail = NULL;
|
||||
}
|
||||
|
||||
void llvm_register_flush_function(flush_fn fn) {
|
||||
struct flush_fn_node *new_node = malloc(sizeof(struct flush_fn_node));
|
||||
new_node->fn = fn;
|
||||
new_node->next = NULL;
|
||||
|
||||
if (!flush_fn_head) {
|
||||
flush_fn_head = flush_fn_tail = new_node;
|
||||
} else {
|
||||
flush_fn_tail->next = new_node;
|
||||
flush_fn_tail = new_node;
|
||||
}
|
||||
}
|
||||
|
||||
void __gcov_flush() {
|
||||
struct flush_fn_node *curr = flush_fn_head;
|
||||
|
||||
while (curr) {
|
||||
curr->fn();
|
||||
curr = curr->next;
|
||||
}
|
||||
}
|
||||
|
||||
void llvm_delete_flush_function_list(void) {
|
||||
while (flush_fn_head) {
|
||||
struct flush_fn_node *node = flush_fn_head;
|
||||
flush_fn_head = flush_fn_head->next;
|
||||
free(node);
|
||||
}
|
||||
|
||||
flush_fn_head = flush_fn_tail = NULL;
|
||||
}
|
||||
|
||||
void llvm_gcov_init(writeout_fn wfn, flush_fn ffn) {
|
||||
static int atexit_ran = 0;
|
||||
|
||||
if (wfn)
|
||||
llvm_register_writeout_function(wfn);
|
||||
|
||||
if (ffn)
|
||||
llvm_register_flush_function(ffn);
|
||||
|
||||
if (atexit_ran == 0) {
|
||||
atexit_ran = 1;
|
||||
|
||||
/* Make sure we write out the data and delete the data structures. */
|
||||
atexit(llvm_delete_flush_function_list);
|
||||
atexit(llvm_delete_writeout_function_list);
|
||||
atexit(llvm_writeout_files);
|
||||
}
|
||||
}
|
740
runtime/profile-rt/profile-rt-70/InstrProfData.inc
Normal file
740
runtime/profile-rt/profile-rt-70/InstrProfData.inc
Normal file
|
@ -0,0 +1,740 @@
|
|||
/*===-- InstrProfData.inc - instr profiling runtime structures -*- C++ -*-=== *\
|
||||
|*
|
||||
|* The LLVM Compiler Infrastructure
|
||||
|*
|
||||
|* This file is distributed under the University of Illinois Open Source
|
||||
|* License. See LICENSE.TXT for details.
|
||||
|*
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
/*
|
||||
* This is the master file that defines all the data structure, signature,
|
||||
* constant literals that are shared across profiling runtime library,
|
||||
* compiler (instrumentation), and host tools (reader/writer). The entities
|
||||
* defined in this file affect the profile runtime ABI, the raw profile format,
|
||||
* or both.
|
||||
*
|
||||
* The file has two identical copies. The master copy lives in LLVM and
|
||||
* the other one sits in compiler-rt/lib/profile directory. To make changes
|
||||
* in this file, first modify the master copy and copy it over to compiler-rt.
|
||||
* Testing of any change in this file can start only after the two copies are
|
||||
* synced up.
|
||||
*
|
||||
* The first part of the file includes macros that defines types, names, and
|
||||
* initializers for the member fields of the core data structures. The field
|
||||
* declarations for one structure is enabled by defining the field activation
|
||||
* macro associated with that structure. Only one field activation record
|
||||
* can be defined at one time and the rest definitions will be filtered out by
|
||||
* the preprocessor.
|
||||
*
|
||||
* Examples of how the template is used to instantiate structure definition:
|
||||
* 1. To declare a structure:
|
||||
*
|
||||
* struct ProfData {
|
||||
* #define INSTR_PROF_DATA(Type, LLVMType, Name, Initializer) \
|
||||
* Type Name;
|
||||
* #include "llvm/ProfileData/InstrProfData.inc"
|
||||
* };
|
||||
*
|
||||
* 2. To construct LLVM type arrays for the struct type:
|
||||
*
|
||||
* Type *DataTypes[] = {
|
||||
* #define INSTR_PROF_DATA(Type, LLVMType, Name, Initializer) \
|
||||
* LLVMType,
|
||||
* #include "llvm/ProfileData/InstrProfData.inc"
|
||||
* };
|
||||
*
|
||||
* 4. To construct constant array for the initializers:
|
||||
* #define INSTR_PROF_DATA(Type, LLVMType, Name, Initializer) \
|
||||
* Initializer,
|
||||
* Constant *ConstantVals[] = {
|
||||
* #include "llvm/ProfileData/InstrProfData.inc"
|
||||
* };
|
||||
*
|
||||
*
|
||||
* The second part of the file includes definitions all other entities that
|
||||
* are related to runtime ABI and format. When no field activation macro is
|
||||
* defined, this file can be included to introduce the definitions.
|
||||
*
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
/* Functions marked with INSTR_PROF_VISIBILITY must have hidden visibility in
|
||||
* the compiler runtime. */
|
||||
#ifndef INSTR_PROF_VISIBILITY
|
||||
#define INSTR_PROF_VISIBILITY
|
||||
#endif
|
||||
|
||||
/* INSTR_PROF_DATA start. */
|
||||
/* Definition of member fields of the per-function control structure. */
|
||||
#ifndef INSTR_PROF_DATA
|
||||
#define INSTR_PROF_DATA(Type, LLVMType, Name, Initializer)
|
||||
#else
|
||||
#define INSTR_PROF_DATA_DEFINED
|
||||
#endif
|
||||
INSTR_PROF_DATA(const uint64_t, llvm::Type::getInt64Ty(Ctx), NameRef, \
|
||||
ConstantInt::get(llvm::Type::getInt64Ty(Ctx), \
|
||||
IndexedInstrProf::ComputeHash(getPGOFuncNameVarInitializer(Inc->getName()))))
|
||||
INSTR_PROF_DATA(const uint64_t, llvm::Type::getInt64Ty(Ctx), FuncHash, \
|
||||
ConstantInt::get(llvm::Type::getInt64Ty(Ctx), \
|
||||
Inc->getHash()->getZExtValue()))
|
||||
INSTR_PROF_DATA(const IntPtrT, llvm::Type::getInt64PtrTy(Ctx), CounterPtr, \
|
||||
ConstantExpr::getBitCast(CounterPtr, \
|
||||
llvm::Type::getInt64PtrTy(Ctx)))
|
||||
/* This is used to map function pointers for the indirect call targets to
|
||||
* function name hashes during the conversion from raw to merged profile
|
||||
* data.
|
||||
*/
|
||||
INSTR_PROF_DATA(const IntPtrT, llvm::Type::getInt8PtrTy(Ctx), FunctionPointer, \
|
||||
FunctionAddr)
|
||||
INSTR_PROF_DATA(IntPtrT, llvm::Type::getInt8PtrTy(Ctx), Values, \
|
||||
ValuesPtrExpr)
|
||||
INSTR_PROF_DATA(const uint32_t, llvm::Type::getInt32Ty(Ctx), NumCounters, \
|
||||
ConstantInt::get(llvm::Type::getInt32Ty(Ctx), NumCounters))
|
||||
INSTR_PROF_DATA(const uint16_t, Int16ArrayTy, NumValueSites[IPVK_Last+1], \
|
||||
ConstantArray::get(Int16ArrayTy, Int16ArrayVals))
|
||||
#undef INSTR_PROF_DATA
|
||||
/* INSTR_PROF_DATA end. */
|
||||
|
||||
|
||||
/* This is an internal data structure used by value profiler. It
|
||||
* is defined here to allow serialization code sharing by LLVM
|
||||
* to be used in unit test.
|
||||
*
|
||||
* typedef struct ValueProfNode {
|
||||
* // InstrProfValueData VData;
|
||||
* uint64_t Value;
|
||||
* uint64_t Count;
|
||||
* struct ValueProfNode *Next;
|
||||
* } ValueProfNode;
|
||||
*/
|
||||
/* INSTR_PROF_VALUE_NODE start. */
|
||||
#ifndef INSTR_PROF_VALUE_NODE
|
||||
#define INSTR_PROF_VALUE_NODE(Type, LLVMType, Name, Initializer)
|
||||
#else
|
||||
#define INSTR_PROF_DATA_DEFINED
|
||||
#endif
|
||||
INSTR_PROF_VALUE_NODE(uint64_t, llvm::Type::getInt64Ty(Ctx), Value, \
|
||||
ConstantInt::get(llvm::Type::GetInt64Ty(Ctx), 0))
|
||||
INSTR_PROF_VALUE_NODE(uint64_t, llvm::Type::getInt64Ty(Ctx), Count, \
|
||||
ConstantInt::get(llvm::Type::GetInt64Ty(Ctx), 0))
|
||||
INSTR_PROF_VALUE_NODE(PtrToNodeT, llvm::Type::getInt8PtrTy(Ctx), Next, \
|
||||
ConstantInt::get(llvm::Type::GetInt8PtrTy(Ctx), 0))
|
||||
#undef INSTR_PROF_VALUE_NODE
|
||||
/* INSTR_PROF_VALUE_NODE end. */
|
||||
|
||||
/* INSTR_PROF_RAW_HEADER start */
|
||||
/* Definition of member fields of the raw profile header data structure. */
|
||||
#ifndef INSTR_PROF_RAW_HEADER
|
||||
#define INSTR_PROF_RAW_HEADER(Type, Name, Initializer)
|
||||
#else
|
||||
#define INSTR_PROF_DATA_DEFINED
|
||||
#endif
|
||||
INSTR_PROF_RAW_HEADER(uint64_t, Magic, __llvm_profile_get_magic())
|
||||
INSTR_PROF_RAW_HEADER(uint64_t, Version, __llvm_profile_get_version())
|
||||
INSTR_PROF_RAW_HEADER(uint64_t, DataSize, DataSize)
|
||||
INSTR_PROF_RAW_HEADER(uint64_t, CountersSize, CountersSize)
|
||||
INSTR_PROF_RAW_HEADER(uint64_t, NamesSize, NamesSize)
|
||||
INSTR_PROF_RAW_HEADER(uint64_t, CountersDelta, (uintptr_t)CountersBegin)
|
||||
INSTR_PROF_RAW_HEADER(uint64_t, NamesDelta, (uintptr_t)NamesBegin)
|
||||
INSTR_PROF_RAW_HEADER(uint64_t, ValueKindLast, IPVK_Last)
|
||||
#undef INSTR_PROF_RAW_HEADER
|
||||
/* INSTR_PROF_RAW_HEADER end */
|
||||
|
||||
/* VALUE_PROF_FUNC_PARAM start */
|
||||
/* Definition of parameter types of the runtime API used to do value profiling
|
||||
* for a given value site.
|
||||
*/
|
||||
#ifndef VALUE_PROF_FUNC_PARAM
|
||||
#define VALUE_PROF_FUNC_PARAM(ArgType, ArgName, ArgLLVMType)
|
||||
#define INSTR_PROF_COMMA
|
||||
#else
|
||||
#define INSTR_PROF_DATA_DEFINED
|
||||
#define INSTR_PROF_COMMA ,
|
||||
#endif
|
||||
VALUE_PROF_FUNC_PARAM(uint64_t, TargetValue, Type::getInt64Ty(Ctx)) \
|
||||
INSTR_PROF_COMMA
|
||||
VALUE_PROF_FUNC_PARAM(void *, Data, Type::getInt8PtrTy(Ctx)) INSTR_PROF_COMMA
|
||||
#ifndef VALUE_RANGE_PROF
|
||||
VALUE_PROF_FUNC_PARAM(uint32_t, CounterIndex, Type::getInt32Ty(Ctx))
|
||||
#else /* VALUE_RANGE_PROF */
|
||||
VALUE_PROF_FUNC_PARAM(uint32_t, CounterIndex, Type::getInt32Ty(Ctx)) \
|
||||
INSTR_PROF_COMMA
|
||||
VALUE_PROF_FUNC_PARAM(uint64_t, PreciseRangeStart, Type::getInt64Ty(Ctx)) \
|
||||
INSTR_PROF_COMMA
|
||||
VALUE_PROF_FUNC_PARAM(uint64_t, PreciseRangeLast, Type::getInt64Ty(Ctx)) \
|
||||
INSTR_PROF_COMMA
|
||||
VALUE_PROF_FUNC_PARAM(uint64_t, LargeValue, Type::getInt64Ty(Ctx))
|
||||
#endif /*VALUE_RANGE_PROF */
|
||||
#undef VALUE_PROF_FUNC_PARAM
|
||||
#undef INSTR_PROF_COMMA
|
||||
/* VALUE_PROF_FUNC_PARAM end */
|
||||
|
||||
/* VALUE_PROF_KIND start */
|
||||
#ifndef VALUE_PROF_KIND
|
||||
#define VALUE_PROF_KIND(Enumerator, Value)
|
||||
#else
|
||||
#define INSTR_PROF_DATA_DEFINED
|
||||
#endif
|
||||
/* For indirect function call value profiling, the addresses of the target
|
||||
* functions are profiled by the instrumented code. The target addresses are
|
||||
* written in the raw profile data and converted to target function name's MD5
|
||||
* hash by the profile reader during deserialization. Typically, this happens
|
||||
* when the the raw profile data is read during profile merging.
|
||||
*
|
||||
* For this remapping the ProfData is used. ProfData contains both the function
|
||||
* name hash and the function address.
|
||||
*/
|
||||
VALUE_PROF_KIND(IPVK_IndirectCallTarget, 0)
|
||||
/* For memory intrinsic functions size profiling. */
|
||||
VALUE_PROF_KIND(IPVK_MemOPSize, 1)
|
||||
/* These two kinds must be the last to be
|
||||
* declared. This is to make sure the string
|
||||
* array created with the template can be
|
||||
* indexed with the kind value.
|
||||
*/
|
||||
VALUE_PROF_KIND(IPVK_First, IPVK_IndirectCallTarget)
|
||||
VALUE_PROF_KIND(IPVK_Last, IPVK_MemOPSize)
|
||||
|
||||
#undef VALUE_PROF_KIND
|
||||
/* VALUE_PROF_KIND end */
|
||||
|
||||
/* COVMAP_FUNC_RECORD start */
|
||||
/* Definition of member fields of the function record structure in coverage
|
||||
* map.
|
||||
*/
|
||||
#ifndef COVMAP_FUNC_RECORD
|
||||
#define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Initializer)
|
||||
#else
|
||||
#define INSTR_PROF_DATA_DEFINED
|
||||
#endif
|
||||
#ifdef COVMAP_V1
|
||||
COVMAP_FUNC_RECORD(const IntPtrT, llvm::Type::getInt8PtrTy(Ctx), \
|
||||
NamePtr, llvm::ConstantExpr::getBitCast(NamePtr, \
|
||||
llvm::Type::getInt8PtrTy(Ctx)))
|
||||
COVMAP_FUNC_RECORD(const uint32_t, llvm::Type::getInt32Ty(Ctx), NameSize, \
|
||||
llvm::ConstantInt::get(llvm::Type::getInt32Ty(Ctx), \
|
||||
NameValue.size()))
|
||||
#else
|
||||
COVMAP_FUNC_RECORD(const int64_t, llvm::Type::getInt64Ty(Ctx), NameRef, \
|
||||
llvm::ConstantInt::get(llvm::Type::getInt64Ty(Ctx), \
|
||||
llvm::IndexedInstrProf::ComputeHash(NameValue)))
|
||||
#endif
|
||||
COVMAP_FUNC_RECORD(const uint32_t, llvm::Type::getInt32Ty(Ctx), DataSize, \
|
||||
llvm::ConstantInt::get(llvm::Type::getInt32Ty(Ctx),\
|
||||
CoverageMapping.size()))
|
||||
COVMAP_FUNC_RECORD(const uint64_t, llvm::Type::getInt64Ty(Ctx), FuncHash, \
|
||||
llvm::ConstantInt::get(llvm::Type::getInt64Ty(Ctx), FuncHash))
|
||||
#undef COVMAP_FUNC_RECORD
|
||||
/* COVMAP_FUNC_RECORD end. */
|
||||
|
||||
/* COVMAP_HEADER start */
|
||||
/* Definition of member fields of coverage map header.
|
||||
*/
|
||||
#ifndef COVMAP_HEADER
|
||||
#define COVMAP_HEADER(Type, LLVMType, Name, Initializer)
|
||||
#else
|
||||
#define INSTR_PROF_DATA_DEFINED
|
||||
#endif
|
||||
COVMAP_HEADER(uint32_t, Int32Ty, NRecords, \
|
||||
llvm::ConstantInt::get(Int32Ty, FunctionRecords.size()))
|
||||
COVMAP_HEADER(uint32_t, Int32Ty, FilenamesSize, \
|
||||
llvm::ConstantInt::get(Int32Ty, FilenamesSize))
|
||||
COVMAP_HEADER(uint32_t, Int32Ty, CoverageSize, \
|
||||
llvm::ConstantInt::get(Int32Ty, CoverageMappingSize))
|
||||
COVMAP_HEADER(uint32_t, Int32Ty, Version, \
|
||||
llvm::ConstantInt::get(Int32Ty, CovMapVersion::CurrentVersion))
|
||||
#undef COVMAP_HEADER
|
||||
/* COVMAP_HEADER end. */
|
||||
|
||||
|
||||
#ifdef INSTR_PROF_SECT_ENTRY
|
||||
#define INSTR_PROF_DATA_DEFINED
|
||||
INSTR_PROF_SECT_ENTRY(IPSK_data, \
|
||||
INSTR_PROF_QUOTE(INSTR_PROF_DATA_COMMON), \
|
||||
INSTR_PROF_QUOTE(INSTR_PROF_DATA_COFF), "__DATA,")
|
||||
INSTR_PROF_SECT_ENTRY(IPSK_cnts, \
|
||||
INSTR_PROF_QUOTE(INSTR_PROF_CNTS_COMMON), \
|
||||
INSTR_PROF_QUOTE(INSTR_PROF_CNTS_COFF), "__DATA,")
|
||||
INSTR_PROF_SECT_ENTRY(IPSK_name, \
|
||||
INSTR_PROF_QUOTE(INSTR_PROF_NAME_COMMON), \
|
||||
INSTR_PROF_QUOTE(INSTR_PROF_NAME_COFF), "__DATA,")
|
||||
INSTR_PROF_SECT_ENTRY(IPSK_vals, \
|
||||
INSTR_PROF_QUOTE(INSTR_PROF_VALS_COMMON), \
|
||||
INSTR_PROF_QUOTE(INSTR_PROF_VALS_COFF), "__DATA,")
|
||||
INSTR_PROF_SECT_ENTRY(IPSK_vnodes, \
|
||||
INSTR_PROF_QUOTE(INSTR_PROF_VNODES_COMMON), \
|
||||
INSTR_PROF_QUOTE(INSTR_PROF_VNODES_COFF), "__DATA,")
|
||||
INSTR_PROF_SECT_ENTRY(IPSK_covmap, \
|
||||
INSTR_PROF_QUOTE(INSTR_PROF_COVMAP_COMMON), \
|
||||
INSTR_PROF_QUOTE(INSTR_PROF_COVMAP_COFF), "__LLVM_COV,")
|
||||
|
||||
#undef INSTR_PROF_SECT_ENTRY
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef INSTR_PROF_VALUE_PROF_DATA
|
||||
#define INSTR_PROF_DATA_DEFINED
|
||||
|
||||
#define INSTR_PROF_MAX_NUM_VAL_PER_SITE 255
|
||||
/*!
|
||||
* This is the header of the data structure that defines the on-disk
|
||||
* layout of the value profile data of a particular kind for one function.
|
||||
*/
|
||||
typedef struct ValueProfRecord {
|
||||
/* The kind of the value profile record. */
|
||||
uint32_t Kind;
|
||||
/*
|
||||
* The number of value profile sites. It is guaranteed to be non-zero;
|
||||
* otherwise the record for this kind won't be emitted.
|
||||
*/
|
||||
uint32_t NumValueSites;
|
||||
/*
|
||||
* The first element of the array that stores the number of profiled
|
||||
* values for each value site. The size of the array is NumValueSites.
|
||||
* Since NumValueSites is greater than zero, there is at least one
|
||||
* element in the array.
|
||||
*/
|
||||
uint8_t SiteCountArray[1];
|
||||
|
||||
/*
|
||||
* The fake declaration is for documentation purpose only.
|
||||
* Align the start of next field to be on 8 byte boundaries.
|
||||
uint8_t Padding[X];
|
||||
*/
|
||||
|
||||
/* The array of value profile data. The size of the array is the sum
|
||||
* of all elements in SiteCountArray[].
|
||||
InstrProfValueData ValueData[];
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
/*!
|
||||
* \brief Return the number of value sites.
|
||||
*/
|
||||
uint32_t getNumValueSites() const { return NumValueSites; }
|
||||
/*!
|
||||
* \brief Read data from this record and save it to Record.
|
||||
*/
|
||||
void deserializeTo(InstrProfRecord &Record,
|
||||
InstrProfRecord::ValueMapType *VMap);
|
||||
/*
|
||||
* In-place byte swap:
|
||||
* Do byte swap for this instance. \c Old is the original order before
|
||||
* the swap, and \c New is the New byte order.
|
||||
*/
|
||||
void swapBytes(support::endianness Old, support::endianness New);
|
||||
#endif
|
||||
} ValueProfRecord;
|
||||
|
||||
/*!
|
||||
* Per-function header/control data structure for value profiling
|
||||
* data in indexed format.
|
||||
*/
|
||||
typedef struct ValueProfData {
|
||||
/*
|
||||
* Total size in bytes including this field. It must be a multiple
|
||||
* of sizeof(uint64_t).
|
||||
*/
|
||||
uint32_t TotalSize;
|
||||
/*
|
||||
*The number of value profile kinds that has value profile data.
|
||||
* In this implementation, a value profile kind is considered to
|
||||
* have profile data if the number of value profile sites for the
|
||||
* kind is not zero. More aggressively, the implementation can
|
||||
* choose to check the actual data value: if none of the value sites
|
||||
* has any profiled values, the kind can be skipped.
|
||||
*/
|
||||
uint32_t NumValueKinds;
|
||||
|
||||
/*
|
||||
* Following are a sequence of variable length records. The prefix/header
|
||||
* of each record is defined by ValueProfRecord type. The number of
|
||||
* records is NumValueKinds.
|
||||
* ValueProfRecord Record_1;
|
||||
* ValueProfRecord Record_N;
|
||||
*/
|
||||
|
||||
#if __cplusplus
|
||||
/*!
|
||||
* Return the total size in bytes of the on-disk value profile data
|
||||
* given the data stored in Record.
|
||||
*/
|
||||
static uint32_t getSize(const InstrProfRecord &Record);
|
||||
/*!
|
||||
* Return a pointer to \c ValueProfData instance ready to be streamed.
|
||||
*/
|
||||
static std::unique_ptr<ValueProfData>
|
||||
serializeFrom(const InstrProfRecord &Record);
|
||||
/*!
|
||||
* Check the integrity of the record.
|
||||
*/
|
||||
Error checkIntegrity();
|
||||
/*!
|
||||
* Return a pointer to \c ValueProfileData instance ready to be read.
|
||||
* All data in the instance are properly byte swapped. The input
|
||||
* data is assumed to be in little endian order.
|
||||
*/
|
||||
static Expected<std::unique_ptr<ValueProfData>>
|
||||
getValueProfData(const unsigned char *SrcBuffer,
|
||||
const unsigned char *const SrcBufferEnd,
|
||||
support::endianness SrcDataEndianness);
|
||||
/*!
|
||||
* Swap byte order from \c Endianness order to host byte order.
|
||||
*/
|
||||
void swapBytesToHost(support::endianness Endianness);
|
||||
/*!
|
||||
* Swap byte order from host byte order to \c Endianness order.
|
||||
*/
|
||||
void swapBytesFromHost(support::endianness Endianness);
|
||||
/*!
|
||||
* Return the total size of \c ValueProfileData.
|
||||
*/
|
||||
uint32_t getSize() const { return TotalSize; }
|
||||
/*!
|
||||
* Read data from this data and save it to \c Record.
|
||||
*/
|
||||
void deserializeTo(InstrProfRecord &Record,
|
||||
InstrProfRecord::ValueMapType *VMap);
|
||||
void operator delete(void *ptr) { ::operator delete(ptr); }
|
||||
#endif
|
||||
} ValueProfData;
|
||||
|
||||
/*
|
||||
* The closure is designed to abstact away two types of value profile data:
|
||||
* - InstrProfRecord which is the primary data structure used to
|
||||
* represent profile data in host tools (reader, writer, and profile-use)
|
||||
* - value profile runtime data structure suitable to be used by C
|
||||
* runtime library.
|
||||
*
|
||||
* Both sources of data need to serialize to disk/memory-buffer in common
|
||||
* format: ValueProfData. The abstraction allows compiler-rt's raw profiler
|
||||
* writer to share the same format and code with indexed profile writer.
|
||||
*
|
||||
* For documentation of the member methods below, refer to corresponding methods
|
||||
* in class InstrProfRecord.
|
||||
*/
|
||||
typedef struct ValueProfRecordClosure {
|
||||
const void *Record;
|
||||
uint32_t (*GetNumValueKinds)(const void *Record);
|
||||
uint32_t (*GetNumValueSites)(const void *Record, uint32_t VKind);
|
||||
uint32_t (*GetNumValueData)(const void *Record, uint32_t VKind);
|
||||
uint32_t (*GetNumValueDataForSite)(const void *R, uint32_t VK, uint32_t S);
|
||||
|
||||
/*
|
||||
* After extracting the value profile data from the value profile record,
|
||||
* this method is used to map the in-memory value to on-disk value. If
|
||||
* the method is null, value will be written out untranslated.
|
||||
*/
|
||||
uint64_t (*RemapValueData)(uint32_t, uint64_t Value);
|
||||
void (*GetValueForSite)(const void *R, InstrProfValueData *Dst, uint32_t K,
|
||||
uint32_t S);
|
||||
ValueProfData *(*AllocValueProfData)(size_t TotalSizeInBytes);
|
||||
} ValueProfRecordClosure;
|
||||
|
||||
INSTR_PROF_VISIBILITY ValueProfRecord *
|
||||
getFirstValueProfRecord(ValueProfData *VPD);
|
||||
INSTR_PROF_VISIBILITY ValueProfRecord *
|
||||
getValueProfRecordNext(ValueProfRecord *VPR);
|
||||
INSTR_PROF_VISIBILITY InstrProfValueData *
|
||||
getValueProfRecordValueData(ValueProfRecord *VPR);
|
||||
INSTR_PROF_VISIBILITY uint32_t
|
||||
getValueProfRecordHeaderSize(uint32_t NumValueSites);
|
||||
|
||||
#undef INSTR_PROF_VALUE_PROF_DATA
|
||||
#endif /* INSTR_PROF_VALUE_PROF_DATA */
|
||||
|
||||
|
||||
#ifdef INSTR_PROF_COMMON_API_IMPL
|
||||
#define INSTR_PROF_DATA_DEFINED
|
||||
#ifdef __cplusplus
|
||||
#define INSTR_PROF_INLINE inline
|
||||
#define INSTR_PROF_NULLPTR nullptr
|
||||
#else
|
||||
#define INSTR_PROF_INLINE
|
||||
#define INSTR_PROF_NULLPTR NULL
|
||||
#endif
|
||||
|
||||
#ifndef offsetof
|
||||
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* \brief Return the \c ValueProfRecord header size including the
|
||||
* padding bytes.
|
||||
*/
|
||||
INSTR_PROF_VISIBILITY INSTR_PROF_INLINE
|
||||
uint32_t getValueProfRecordHeaderSize(uint32_t NumValueSites) {
|
||||
uint32_t Size = offsetof(ValueProfRecord, SiteCountArray) +
|
||||
sizeof(uint8_t) * NumValueSites;
|
||||
/* Round the size to multiple of 8 bytes. */
|
||||
Size = (Size + 7) & ~7;
|
||||
return Size;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Return the total size of the value profile record including the
|
||||
* header and the value data.
|
||||
*/
|
||||
INSTR_PROF_VISIBILITY INSTR_PROF_INLINE
|
||||
uint32_t getValueProfRecordSize(uint32_t NumValueSites,
|
||||
uint32_t NumValueData) {
|
||||
return getValueProfRecordHeaderSize(NumValueSites) +
|
||||
sizeof(InstrProfValueData) * NumValueData;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Return the pointer to the start of value data array.
|
||||
*/
|
||||
INSTR_PROF_VISIBILITY INSTR_PROF_INLINE
|
||||
InstrProfValueData *getValueProfRecordValueData(ValueProfRecord *This) {
|
||||
return (InstrProfValueData *)((char *)This + getValueProfRecordHeaderSize(
|
||||
This->NumValueSites));
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Return the total number of value data for \c This record.
|
||||
*/
|
||||
INSTR_PROF_VISIBILITY INSTR_PROF_INLINE
|
||||
uint32_t getValueProfRecordNumValueData(ValueProfRecord *This) {
|
||||
uint32_t NumValueData = 0;
|
||||
uint32_t I;
|
||||
for (I = 0; I < This->NumValueSites; I++)
|
||||
NumValueData += This->SiteCountArray[I];
|
||||
return NumValueData;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Use this method to advance to the next \c This \c ValueProfRecord.
|
||||
*/
|
||||
INSTR_PROF_VISIBILITY INSTR_PROF_INLINE
|
||||
ValueProfRecord *getValueProfRecordNext(ValueProfRecord *This) {
|
||||
uint32_t NumValueData = getValueProfRecordNumValueData(This);
|
||||
return (ValueProfRecord *)((char *)This +
|
||||
getValueProfRecordSize(This->NumValueSites,
|
||||
NumValueData));
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Return the first \c ValueProfRecord instance.
|
||||
*/
|
||||
INSTR_PROF_VISIBILITY INSTR_PROF_INLINE
|
||||
ValueProfRecord *getFirstValueProfRecord(ValueProfData *This) {
|
||||
return (ValueProfRecord *)((char *)This + sizeof(ValueProfData));
|
||||
}
|
||||
|
||||
/* Closure based interfaces. */
|
||||
|
||||
/*!
|
||||
* Return the total size in bytes of the on-disk value profile data
|
||||
* given the data stored in Record.
|
||||
*/
|
||||
INSTR_PROF_VISIBILITY uint32_t
|
||||
getValueProfDataSize(ValueProfRecordClosure *Closure) {
|
||||
uint32_t Kind;
|
||||
uint32_t TotalSize = sizeof(ValueProfData);
|
||||
const void *Record = Closure->Record;
|
||||
|
||||
for (Kind = IPVK_First; Kind <= IPVK_Last; Kind++) {
|
||||
uint32_t NumValueSites = Closure->GetNumValueSites(Record, Kind);
|
||||
if (!NumValueSites)
|
||||
continue;
|
||||
TotalSize += getValueProfRecordSize(NumValueSites,
|
||||
Closure->GetNumValueData(Record, Kind));
|
||||
}
|
||||
return TotalSize;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Extract value profile data of a function for the profile kind \c ValueKind
|
||||
* from the \c Closure and serialize the data into \c This record instance.
|
||||
*/
|
||||
INSTR_PROF_VISIBILITY void
|
||||
serializeValueProfRecordFrom(ValueProfRecord *This,
|
||||
ValueProfRecordClosure *Closure,
|
||||
uint32_t ValueKind, uint32_t NumValueSites) {
|
||||
uint32_t S;
|
||||
const void *Record = Closure->Record;
|
||||
This->Kind = ValueKind;
|
||||
This->NumValueSites = NumValueSites;
|
||||
InstrProfValueData *DstVD = getValueProfRecordValueData(This);
|
||||
|
||||
for (S = 0; S < NumValueSites; S++) {
|
||||
uint32_t ND = Closure->GetNumValueDataForSite(Record, ValueKind, S);
|
||||
This->SiteCountArray[S] = ND;
|
||||
Closure->GetValueForSite(Record, DstVD, ValueKind, S);
|
||||
DstVD += ND;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Extract value profile data of a function from the \c Closure
|
||||
* and serialize the data into \c DstData if it is not NULL or heap
|
||||
* memory allocated by the \c Closure's allocator method. If \c
|
||||
* DstData is not null, the caller is expected to set the TotalSize
|
||||
* in DstData.
|
||||
*/
|
||||
INSTR_PROF_VISIBILITY ValueProfData *
|
||||
serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
|
||||
ValueProfData *DstData) {
|
||||
uint32_t Kind;
|
||||
uint32_t TotalSize =
|
||||
DstData ? DstData->TotalSize : getValueProfDataSize(Closure);
|
||||
|
||||
ValueProfData *VPD =
|
||||
DstData ? DstData : Closure->AllocValueProfData(TotalSize);
|
||||
|
||||
VPD->TotalSize = TotalSize;
|
||||
VPD->NumValueKinds = Closure->GetNumValueKinds(Closure->Record);
|
||||
ValueProfRecord *VR = getFirstValueProfRecord(VPD);
|
||||
for (Kind = IPVK_First; Kind <= IPVK_Last; Kind++) {
|
||||
uint32_t NumValueSites = Closure->GetNumValueSites(Closure->Record, Kind);
|
||||
if (!NumValueSites)
|
||||
continue;
|
||||
serializeValueProfRecordFrom(VR, Closure, Kind, NumValueSites);
|
||||
VR = getValueProfRecordNext(VR);
|
||||
}
|
||||
return VPD;
|
||||
}
|
||||
|
||||
#undef INSTR_PROF_COMMON_API_IMPL
|
||||
#endif /* INSTR_PROF_COMMON_API_IMPL */
|
||||
|
||||
/*============================================================================*/
|
||||
|
||||
#ifndef INSTR_PROF_DATA_DEFINED
|
||||
|
||||
#ifndef INSTR_PROF_DATA_INC
|
||||
#define INSTR_PROF_DATA_INC
|
||||
|
||||
/* Helper macros. */
|
||||
#define INSTR_PROF_SIMPLE_QUOTE(x) #x
|
||||
#define INSTR_PROF_QUOTE(x) INSTR_PROF_SIMPLE_QUOTE(x)
|
||||
#define INSTR_PROF_SIMPLE_CONCAT(x,y) x ## y
|
||||
#define INSTR_PROF_CONCAT(x,y) INSTR_PROF_SIMPLE_CONCAT(x,y)
|
||||
|
||||
/* Magic number to detect file format and endianness.
|
||||
* Use 255 at one end, since no UTF-8 file can use that character. Avoid 0,
|
||||
* so that utilities, like strings, don't grab it as a string. 129 is also
|
||||
* invalid UTF-8, and high enough to be interesting.
|
||||
* Use "lprofr" in the centre to stand for "LLVM Profile Raw", or "lprofR"
|
||||
* for 32-bit platforms.
|
||||
*/
|
||||
#define INSTR_PROF_RAW_MAGIC_64 (uint64_t)255 << 56 | (uint64_t)'l' << 48 | \
|
||||
(uint64_t)'p' << 40 | (uint64_t)'r' << 32 | (uint64_t)'o' << 24 | \
|
||||
(uint64_t)'f' << 16 | (uint64_t)'r' << 8 | (uint64_t)129
|
||||
#define INSTR_PROF_RAW_MAGIC_32 (uint64_t)255 << 56 | (uint64_t)'l' << 48 | \
|
||||
(uint64_t)'p' << 40 | (uint64_t)'r' << 32 | (uint64_t)'o' << 24 | \
|
||||
(uint64_t)'f' << 16 | (uint64_t)'R' << 8 | (uint64_t)129
|
||||
|
||||
/* Raw profile format version (start from 1). */
|
||||
#define INSTR_PROF_RAW_VERSION 4
|
||||
/* Indexed profile format version (start from 1). */
|
||||
#define INSTR_PROF_INDEX_VERSION 4
|
||||
/* Coverage mapping format vresion (start from 0). */
|
||||
#define INSTR_PROF_COVMAP_VERSION 1
|
||||
|
||||
/* Profile version is always of type uint64_t. Reserve the upper 8 bits in the
|
||||
* version for other variants of profile. We set the lowest bit of the upper 8
|
||||
* bits (i.e. bit 56) to 1 to indicate if this is an IR-level instrumentaiton
|
||||
* generated profile, and 0 if this is a Clang FE generated profile.
|
||||
*/
|
||||
#define VARIANT_MASKS_ALL 0xff00000000000000ULL
|
||||
#define GET_VERSION(V) ((V) & ~VARIANT_MASKS_ALL)
|
||||
#define VARIANT_MASK_IR_PROF (0x1ULL << 56)
|
||||
#define INSTR_PROF_RAW_VERSION_VAR __llvm_profile_raw_version
|
||||
#define INSTR_PROF_PROFILE_RUNTIME_VAR __llvm_profile_runtime
|
||||
|
||||
/* The variable that holds the name of the profile data
|
||||
* specified via command line. */
|
||||
#define INSTR_PROF_PROFILE_NAME_VAR __llvm_profile_filename
|
||||
|
||||
/* section name strings common to all targets other
|
||||
than WIN32 */
|
||||
#define INSTR_PROF_DATA_COMMON __llvm_prf_data
|
||||
#define INSTR_PROF_NAME_COMMON __llvm_prf_names
|
||||
#define INSTR_PROF_CNTS_COMMON __llvm_prf_cnts
|
||||
#define INSTR_PROF_VALS_COMMON __llvm_prf_vals
|
||||
#define INSTR_PROF_VNODES_COMMON __llvm_prf_vnds
|
||||
#define INSTR_PROF_COVMAP_COMMON __llvm_covmap
|
||||
/* Win32 */
|
||||
#define INSTR_PROF_DATA_COFF .lprfd
|
||||
#define INSTR_PROF_NAME_COFF .lprfn
|
||||
#define INSTR_PROF_CNTS_COFF .lprfc
|
||||
#define INSTR_PROF_VALS_COFF .lprfv
|
||||
#define INSTR_PROF_VNODES_COFF .lprfnd
|
||||
#define INSTR_PROF_COVMAP_COFF .lcovmap
|
||||
|
||||
#ifdef _WIN32
|
||||
/* Runtime section names and name strings. */
|
||||
#define INSTR_PROF_DATA_SECT_NAME INSTR_PROF_DATA_COFF
|
||||
#define INSTR_PROF_NAME_SECT_NAME INSTR_PROF_NAME_COFF
|
||||
#define INSTR_PROF_CNTS_SECT_NAME INSTR_PROF_CNTS_COFF
|
||||
/* Array of pointers. Each pointer points to a list
|
||||
* of value nodes associated with one value site.
|
||||
*/
|
||||
#define INSTR_PROF_VALS_SECT_NAME INSTR_PROF_VALS_COFF
|
||||
/* Value profile nodes section. */
|
||||
#define INSTR_PROF_VNODES_SECT_NAME INSTR_PROF_VNODES_COFF
|
||||
#define INSTR_PROF_COVMAP_SECT_NAME INSTR_PROF_COVMAP_COFF
|
||||
#else
|
||||
/* Runtime section names and name strings. */
|
||||
#define INSTR_PROF_DATA_SECT_NAME INSTR_PROF_DATA_COMMON
|
||||
#define INSTR_PROF_NAME_SECT_NAME INSTR_PROF_NAME_COMMON
|
||||
#define INSTR_PROF_CNTS_SECT_NAME INSTR_PROF_CNTS_COMMON
|
||||
/* Array of pointers. Each pointer points to a list
|
||||
* of value nodes associated with one value site.
|
||||
*/
|
||||
#define INSTR_PROF_VALS_SECT_NAME INSTR_PROF_VALS_COMMON
|
||||
/* Value profile nodes section. */
|
||||
#define INSTR_PROF_VNODES_SECT_NAME INSTR_PROF_VNODES_COMMON
|
||||
#define INSTR_PROF_COVMAP_SECT_NAME INSTR_PROF_COVMAP_COMMON
|
||||
#endif
|
||||
|
||||
#define INSTR_PROF_DATA_SECT_NAME_STR \
|
||||
INSTR_PROF_QUOTE(INSTR_PROF_DATA_SECT_NAME)
|
||||
#define INSTR_PROF_NAME_SECT_NAME_STR \
|
||||
INSTR_PROF_QUOTE(INSTR_PROF_NAME_SECT_NAME)
|
||||
#define INSTR_PROF_CNTS_SECT_NAME_STR \
|
||||
INSTR_PROF_QUOTE(INSTR_PROF_CNTS_SECT_NAME)
|
||||
#define INSTR_PROF_COVMAP_SECT_NAME_STR \
|
||||
INSTR_PROF_QUOTE(INSTR_PROF_COVMAP_SECT_NAME)
|
||||
#define INSTR_PROF_VALS_SECT_NAME_STR \
|
||||
INSTR_PROF_QUOTE(INSTR_PROF_VALS_SECT_NAME)
|
||||
#define INSTR_PROF_VNODES_SECT_NAME_STR \
|
||||
INSTR_PROF_QUOTE(INSTR_PROF_VNODES_SECT_NAME)
|
||||
|
||||
/* Macros to define start/stop section symbol for a given
|
||||
* section on Linux. For instance
|
||||
* INSTR_PROF_SECT_START(INSTR_PROF_DATA_SECT_NAME) will
|
||||
* expand to __start___llvm_prof_data
|
||||
*/
|
||||
#define INSTR_PROF_SECT_START(Sect) \
|
||||
INSTR_PROF_CONCAT(__start_,Sect)
|
||||
#define INSTR_PROF_SECT_STOP(Sect) \
|
||||
INSTR_PROF_CONCAT(__stop_,Sect)
|
||||
|
||||
/* Value Profiling API linkage name. */
|
||||
#define INSTR_PROF_VALUE_PROF_FUNC __llvm_profile_instrument_target
|
||||
#define INSTR_PROF_VALUE_PROF_FUNC_STR \
|
||||
INSTR_PROF_QUOTE(INSTR_PROF_VALUE_PROF_FUNC)
|
||||
#define INSTR_PROF_VALUE_RANGE_PROF_FUNC __llvm_profile_instrument_range
|
||||
#define INSTR_PROF_VALUE_RANGE_PROF_FUNC_STR \
|
||||
INSTR_PROF_QUOTE(INSTR_PROF_VALUE_RANGE_PROF_FUNC)
|
||||
|
||||
/* InstrProfile per-function control data alignment. */
|
||||
#define INSTR_PROF_DATA_ALIGNMENT 8
|
||||
|
||||
/* The data structure that represents a tracked value by the
|
||||
* value profiler.
|
||||
*/
|
||||
typedef struct InstrProfValueData {
|
||||
/* Profiled value. */
|
||||
uint64_t Value;
|
||||
/* Number of times the value appears in the training run. */
|
||||
uint64_t Count;
|
||||
} InstrProfValueData;
|
||||
|
||||
#endif /* INSTR_PROF_DATA_INC */
|
||||
|
||||
#else
|
||||
#undef INSTR_PROF_DATA_DEFINED
|
||||
#endif
|
79
runtime/profile-rt/profile-rt-70/InstrProfiling.c
Normal file
79
runtime/profile-rt/profile-rt-70/InstrProfiling.c
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*===- InstrProfiling.c - Support library for PGO instrumentation ---------===*\
|
||||
|*
|
||||
|* The LLVM Compiler Infrastructure
|
||||
|*
|
||||
|* This file is distributed under the University of Illinois Open Source
|
||||
|* License. See LICENSE.TXT for details.
|
||||
|*
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
#include "InstrProfiling.h"
|
||||
#include "InstrProfilingInternal.h"
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#define INSTR_PROF_VALUE_PROF_DATA
|
||||
#include "InstrProfData.inc"
|
||||
|
||||
|
||||
COMPILER_RT_WEAK uint64_t INSTR_PROF_RAW_VERSION_VAR = INSTR_PROF_RAW_VERSION;
|
||||
|
||||
COMPILER_RT_VISIBILITY uint64_t __llvm_profile_get_magic(void) {
|
||||
return sizeof(void *) == sizeof(uint64_t) ? (INSTR_PROF_RAW_MAGIC_64)
|
||||
: (INSTR_PROF_RAW_MAGIC_32);
|
||||
}
|
||||
|
||||
static unsigned ProfileDumped = 0;
|
||||
|
||||
COMPILER_RT_VISIBILITY unsigned lprofProfileDumped() {
|
||||
return ProfileDumped;
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY void lprofSetProfileDumped() {
|
||||
ProfileDumped = 1;
|
||||
}
|
||||
|
||||
/* Return the number of bytes needed to add to SizeInBytes to make it
|
||||
* the result a multiple of 8.
|
||||
*/
|
||||
COMPILER_RT_VISIBILITY uint8_t
|
||||
__llvm_profile_get_num_padding_bytes(uint64_t SizeInBytes) {
|
||||
return 7 & (sizeof(uint64_t) - SizeInBytes % sizeof(uint64_t));
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY uint64_t __llvm_profile_get_version(void) {
|
||||
return __llvm_profile_raw_version;
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY void __llvm_profile_reset_counters(void) {
|
||||
uint64_t *I = __llvm_profile_begin_counters();
|
||||
uint64_t *E = __llvm_profile_end_counters();
|
||||
|
||||
memset(I, 0, sizeof(uint64_t) * (E - I));
|
||||
|
||||
const __llvm_profile_data *DataBegin = __llvm_profile_begin_data();
|
||||
const __llvm_profile_data *DataEnd = __llvm_profile_end_data();
|
||||
const __llvm_profile_data *DI;
|
||||
for (DI = DataBegin; DI < DataEnd; ++DI) {
|
||||
uint64_t CurrentVSiteCount = 0;
|
||||
uint32_t VKI, i;
|
||||
if (!DI->Values)
|
||||
continue;
|
||||
|
||||
ValueProfNode **ValueCounters = (ValueProfNode **)DI->Values;
|
||||
|
||||
for (VKI = IPVK_First; VKI <= IPVK_Last; ++VKI)
|
||||
CurrentVSiteCount += DI->NumValueSites[VKI];
|
||||
|
||||
for (i = 0; i < CurrentVSiteCount; ++i) {
|
||||
ValueProfNode *CurrentVNode = ValueCounters[i];
|
||||
|
||||
while (CurrentVNode) {
|
||||
CurrentVNode->Count = 0;
|
||||
CurrentVNode = CurrentVNode->Next;
|
||||
}
|
||||
}
|
||||
}
|
||||
ProfileDumped = 0;
|
||||
}
|
209
runtime/profile-rt/profile-rt-70/InstrProfiling.h
Normal file
209
runtime/profile-rt/profile-rt-70/InstrProfiling.h
Normal file
|
@ -0,0 +1,209 @@
|
|||
/*===- InstrProfiling.h- Support library for PGO instrumentation ----------===*\
|
||||
|*
|
||||
|* The LLVM Compiler Infrastructure
|
||||
|*
|
||||
|* This file is distributed under the University of Illinois Open Source
|
||||
|* License. See LICENSE.TXT for details.
|
||||
|*
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
#ifndef PROFILE_INSTRPROFILING_H_
|
||||
#define PROFILE_INSTRPROFILING_H_
|
||||
|
||||
#include "InstrProfilingPort.h"
|
||||
|
||||
#define INSTR_PROF_VISIBILITY COMPILER_RT_VISIBILITY
|
||||
#include "InstrProfData.inc"
|
||||
|
||||
enum ValueKind {
|
||||
#define VALUE_PROF_KIND(Enumerator, Value) Enumerator = Value,
|
||||
#include "InstrProfData.inc"
|
||||
};
|
||||
|
||||
typedef void *IntPtrT;
|
||||
typedef struct COMPILER_RT_ALIGNAS(INSTR_PROF_DATA_ALIGNMENT)
|
||||
__llvm_profile_data {
|
||||
#define INSTR_PROF_DATA(Type, LLVMType, Name, Initializer) Type Name;
|
||||
#include "InstrProfData.inc"
|
||||
} __llvm_profile_data;
|
||||
|
||||
typedef struct __llvm_profile_header {
|
||||
#define INSTR_PROF_RAW_HEADER(Type, Name, Initializer) Type Name;
|
||||
#include "InstrProfData.inc"
|
||||
} __llvm_profile_header;
|
||||
|
||||
typedef struct ValueProfNode * PtrToNodeT;
|
||||
typedef struct ValueProfNode {
|
||||
#define INSTR_PROF_VALUE_NODE(Type, LLVMType, Name, Initializer) Type Name;
|
||||
#include "InstrProfData.inc"
|
||||
} ValueProfNode;
|
||||
|
||||
/*!
|
||||
* \brief Get number of bytes necessary to pad the argument to eight
|
||||
* byte boundary.
|
||||
*/
|
||||
uint8_t __llvm_profile_get_num_padding_bytes(uint64_t SizeInBytes);
|
||||
|
||||
/*!
|
||||
* \brief Get required size for profile buffer.
|
||||
*/
|
||||
uint64_t __llvm_profile_get_size_for_buffer(void);
|
||||
|
||||
/*!
|
||||
* \brief Write instrumentation data to the given buffer.
|
||||
*
|
||||
* \pre \c Buffer is the start of a buffer at least as big as \a
|
||||
* __llvm_profile_get_size_for_buffer().
|
||||
*/
|
||||
int __llvm_profile_write_buffer(char *Buffer);
|
||||
|
||||
const __llvm_profile_data *__llvm_profile_begin_data(void);
|
||||
const __llvm_profile_data *__llvm_profile_end_data(void);
|
||||
const char *__llvm_profile_begin_names(void);
|
||||
const char *__llvm_profile_end_names(void);
|
||||
uint64_t *__llvm_profile_begin_counters(void);
|
||||
uint64_t *__llvm_profile_end_counters(void);
|
||||
ValueProfNode *__llvm_profile_begin_vnodes();
|
||||
ValueProfNode *__llvm_profile_end_vnodes();
|
||||
|
||||
/*!
|
||||
* \brief Clear profile counters to zero.
|
||||
*
|
||||
*/
|
||||
void __llvm_profile_reset_counters(void);
|
||||
|
||||
/*!
|
||||
* \brief Merge profile data from buffer.
|
||||
*
|
||||
* Read profile data form buffer \p Profile and merge with
|
||||
* in-process profile counters. The client is expected to
|
||||
* have checked or already knows the profile data in the
|
||||
* buffer matches the in-process counter structure before
|
||||
* calling it.
|
||||
*/
|
||||
void __llvm_profile_merge_from_buffer(const char *Profile, uint64_t Size);
|
||||
|
||||
/*! \brief Check if profile in buffer matches the current binary.
|
||||
*
|
||||
* Returns 0 (success) if the profile data in buffer \p Profile with size
|
||||
* \p Size was generated by the same binary and therefore matches
|
||||
* structurally the in-process counters. If the profile data in buffer is
|
||||
* not compatible, the interface returns 1 (failure).
|
||||
*/
|
||||
int __llvm_profile_check_compatibility(const char *Profile,
|
||||
uint64_t Size);
|
||||
|
||||
/*!
|
||||
* \brief Counts the number of times a target value is seen.
|
||||
*
|
||||
* Records the target value for the CounterIndex if not seen before. Otherwise,
|
||||
* increments the counter associated w/ the target value.
|
||||
* void __llvm_profile_instrument_target(uint64_t TargetValue, void *Data,
|
||||
* uint32_t CounterIndex);
|
||||
*/
|
||||
void INSTR_PROF_VALUE_PROF_FUNC(
|
||||
#define VALUE_PROF_FUNC_PARAM(ArgType, ArgName, ArgLLVMType) ArgType ArgName
|
||||
#include "InstrProfData.inc"
|
||||
);
|
||||
|
||||
/*!
|
||||
* \brief Write instrumentation data to the current file.
|
||||
*
|
||||
* Writes to the file with the last name given to \a *
|
||||
* __llvm_profile_set_filename(),
|
||||
* or if it hasn't been called, the \c LLVM_PROFILE_FILE environment variable,
|
||||
* or if that's not set, the last name set to INSTR_PROF_PROFILE_NAME_VAR,
|
||||
* or if that's not set, \c "default.profraw".
|
||||
*/
|
||||
int __llvm_profile_write_file(void);
|
||||
|
||||
/*!
|
||||
* \brief this is a wrapper interface to \c __llvm_profile_write_file.
|
||||
* After this interface is invoked, a arleady dumped flag will be set
|
||||
* so that profile won't be dumped again during program exit.
|
||||
* Invocation of interface __llvm_profile_reset_counters will clear
|
||||
* the flag. This interface is designed to be used to collect profile
|
||||
* data from user selected hot regions. The use model is
|
||||
* __llvm_profile_reset_counters();
|
||||
* ... hot region 1
|
||||
* __llvm_profile_dump();
|
||||
* .. some other code
|
||||
* __llvm_profile_reset_counters();
|
||||
* ... hot region 2
|
||||
* __llvm_profile_dump();
|
||||
*
|
||||
* It is expected that on-line profile merging is on with \c %m specifier
|
||||
* used in profile filename . If merging is not turned on, user is expected
|
||||
* to invoke __llvm_profile_set_filename to specify different profile names
|
||||
* for different regions before dumping to avoid profile write clobbering.
|
||||
*/
|
||||
int __llvm_profile_dump(void);
|
||||
|
||||
/*!
|
||||
* \brief Set the filename for writing instrumentation data.
|
||||
*
|
||||
* Sets the filename to be used for subsequent calls to
|
||||
* \a __llvm_profile_write_file().
|
||||
*
|
||||
* \c Name is not copied, so it must remain valid. Passing NULL resets the
|
||||
* filename logic to the default behaviour.
|
||||
*/
|
||||
void __llvm_profile_set_filename(const char *Name);
|
||||
|
||||
/*! \brief Register to write instrumentation data to file at exit. */
|
||||
int __llvm_profile_register_write_file_atexit(void);
|
||||
|
||||
/*! \brief Initialize file handling. */
|
||||
void __llvm_profile_initialize_file(void);
|
||||
|
||||
/*!
|
||||
* \brief Return path prefix (excluding the base filename) of the profile data.
|
||||
* This is useful for users using \c -fprofile-generate=./path_prefix who do
|
||||
* not care about the default raw profile name. It is also useful to collect
|
||||
* more than more profile data files dumped in the same directory (Online
|
||||
* merge mode is turned on for instrumented programs with shared libs).
|
||||
* Side-effect: this API call will invoke malloc with dynamic memory allocation.
|
||||
*/
|
||||
const char *__llvm_profile_get_path_prefix();
|
||||
|
||||
/*! \brief Get the magic token for the file format. */
|
||||
uint64_t __llvm_profile_get_magic(void);
|
||||
|
||||
/*! \brief Get the version of the file format. */
|
||||
uint64_t __llvm_profile_get_version(void);
|
||||
|
||||
/*! \brief Get the number of entries in the profile data section. */
|
||||
uint64_t __llvm_profile_get_data_size(const __llvm_profile_data *Begin,
|
||||
const __llvm_profile_data *End);
|
||||
|
||||
/*!
|
||||
* This variable is defined in InstrProfilingRuntime.cc as a hidden
|
||||
* symbol. Its main purpose is to enable profile runtime user to
|
||||
* bypass runtime initialization code -- if the client code explicitly
|
||||
* define this variable, then InstProfileRuntime.o won't be linked in.
|
||||
* Note that this variable's visibility needs to be hidden so that the
|
||||
* definition of this variable in an instrumented shared library won't
|
||||
* affect runtime initialization decision of the main program.
|
||||
* __llvm_profile_profile_runtime. */
|
||||
COMPILER_RT_VISIBILITY extern int INSTR_PROF_PROFILE_RUNTIME_VAR;
|
||||
|
||||
/*!
|
||||
* This variable is defined in InstrProfiling.c. Its main purpose is to
|
||||
* encode the raw profile version value and other format related information
|
||||
* such as whether the profile is from IR based instrumentation. The variable
|
||||
* is defined as weak so that compiler can emit an overriding definition
|
||||
* depending on user option. Since we don't support mixing FE and IR based
|
||||
* data in the same raw profile data file (in other words, shared libs and
|
||||
* main program are expected to be instrumented in the same way), there is
|
||||
* no need for this variable to be hidden.
|
||||
*/
|
||||
extern uint64_t INSTR_PROF_RAW_VERSION_VAR; /* __llvm_profile_raw_version */
|
||||
|
||||
/*!
|
||||
* This variable is a weak symbol defined in InstrProfiling.c. It allows
|
||||
* compiler instrumentation to provide overriding definition with value
|
||||
* from compiler command line. This variable has default visibility.
|
||||
*/
|
||||
extern char INSTR_PROF_PROFILE_NAME_VAR[1]; /* __llvm_profile_filename. */
|
||||
|
||||
#endif /* PROFILE_INSTRPROFILING_H_ */
|
68
runtime/profile-rt/profile-rt-70/InstrProfilingBuffer.c
Normal file
68
runtime/profile-rt/profile-rt-70/InstrProfilingBuffer.c
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*===- InstrProfilingBuffer.c - Write instrumentation to a memory buffer --===*\
|
||||
|*
|
||||
|* The LLVM Compiler Infrastructure
|
||||
|*
|
||||
|* This file is distributed under the University of Illinois Open Source
|
||||
|* License. See LICENSE.TXT for details.
|
||||
|*
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
#include "InstrProfiling.h"
|
||||
#include "InstrProfilingInternal.h"
|
||||
|
||||
COMPILER_RT_VISIBILITY
|
||||
uint64_t __llvm_profile_get_size_for_buffer(void) {
|
||||
const __llvm_profile_data *DataBegin = __llvm_profile_begin_data();
|
||||
const __llvm_profile_data *DataEnd = __llvm_profile_end_data();
|
||||
const uint64_t *CountersBegin = __llvm_profile_begin_counters();
|
||||
const uint64_t *CountersEnd = __llvm_profile_end_counters();
|
||||
const char *NamesBegin = __llvm_profile_begin_names();
|
||||
const char *NamesEnd = __llvm_profile_end_names();
|
||||
|
||||
return __llvm_profile_get_size_for_buffer_internal(
|
||||
DataBegin, DataEnd, CountersBegin, CountersEnd, NamesBegin, NamesEnd);
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY
|
||||
uint64_t __llvm_profile_get_data_size(const __llvm_profile_data *Begin,
|
||||
const __llvm_profile_data *End) {
|
||||
intptr_t BeginI = (intptr_t)Begin, EndI = (intptr_t)End;
|
||||
return ((EndI + sizeof(__llvm_profile_data) - 1) - BeginI) /
|
||||
sizeof(__llvm_profile_data);
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY
|
||||
uint64_t __llvm_profile_get_size_for_buffer_internal(
|
||||
const __llvm_profile_data *DataBegin, const __llvm_profile_data *DataEnd,
|
||||
const uint64_t *CountersBegin, const uint64_t *CountersEnd,
|
||||
const char *NamesBegin, const char *NamesEnd) {
|
||||
/* Match logic in __llvm_profile_write_buffer(). */
|
||||
const uint64_t NamesSize = (NamesEnd - NamesBegin) * sizeof(char);
|
||||
const uint8_t Padding = __llvm_profile_get_num_padding_bytes(NamesSize);
|
||||
return sizeof(__llvm_profile_header) +
|
||||
(__llvm_profile_get_data_size(DataBegin, DataEnd) *
|
||||
sizeof(__llvm_profile_data)) +
|
||||
(CountersEnd - CountersBegin) * sizeof(uint64_t) + NamesSize + Padding;
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY
|
||||
void initBufferWriter(ProfDataWriter *BufferWriter, char *Buffer) {
|
||||
BufferWriter->Write = lprofBufferWriter;
|
||||
BufferWriter->WriterCtx = Buffer;
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY int __llvm_profile_write_buffer(char *Buffer) {
|
||||
ProfDataWriter BufferWriter;
|
||||
initBufferWriter(&BufferWriter, Buffer);
|
||||
return lprofWriteData(&BufferWriter, 0, 0);
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY int __llvm_profile_write_buffer_internal(
|
||||
char *Buffer, const __llvm_profile_data *DataBegin,
|
||||
const __llvm_profile_data *DataEnd, const uint64_t *CountersBegin,
|
||||
const uint64_t *CountersEnd, const char *NamesBegin, const char *NamesEnd) {
|
||||
ProfDataWriter BufferWriter;
|
||||
initBufferWriter(&BufferWriter, Buffer);
|
||||
return lprofWriteDataImpl(&BufferWriter, DataBegin, DataEnd, CountersBegin,
|
||||
CountersEnd, 0, NamesBegin, NamesEnd, 0);
|
||||
}
|
622
runtime/profile-rt/profile-rt-70/InstrProfilingFile.c
Normal file
622
runtime/profile-rt/profile-rt-70/InstrProfilingFile.c
Normal file
|
@ -0,0 +1,622 @@
|
|||
/*===- InstrProfilingFile.c - Write instrumentation to a file -------------===*\
|
||||
|*
|
||||
|* The LLVM Compiler Infrastructure
|
||||
|*
|
||||
|* This file is distributed under the University of Illinois Open Source
|
||||
|* License. See LICENSE.TXT for details.
|
||||
|*
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
#include "InstrProfiling.h"
|
||||
#include "InstrProfilingInternal.h"
|
||||
#include "InstrProfilingUtil.h"
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef _MSC_VER
|
||||
/* For _alloca. */
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
#if defined(_WIN32)
|
||||
#include "WindowsMMap.h"
|
||||
/* For _chsize_s */
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <sys/file.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#if defined(__linux__)
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* From where is profile name specified.
|
||||
* The order the enumerators define their
|
||||
* precedence. Re-order them may lead to
|
||||
* runtime behavior change. */
|
||||
typedef enum ProfileNameSpecifier {
|
||||
PNS_unknown = 0,
|
||||
PNS_default,
|
||||
PNS_command_line,
|
||||
PNS_environment,
|
||||
PNS_runtime_api
|
||||
} ProfileNameSpecifier;
|
||||
|
||||
static const char *getPNSStr(ProfileNameSpecifier PNS) {
|
||||
switch (PNS) {
|
||||
case PNS_default:
|
||||
return "default setting";
|
||||
case PNS_command_line:
|
||||
return "command line";
|
||||
case PNS_environment:
|
||||
return "environment variable";
|
||||
case PNS_runtime_api:
|
||||
return "runtime API";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
#define MAX_PID_SIZE 16
|
||||
/* Data structure holding the result of parsed filename pattern. */
|
||||
typedef struct lprofFilename {
|
||||
/* File name string possibly with %p or %h specifiers. */
|
||||
const char *FilenamePat;
|
||||
/* A flag indicating if FilenamePat's memory is allocated
|
||||
* by runtime. */
|
||||
unsigned OwnsFilenamePat;
|
||||
const char *ProfilePathPrefix;
|
||||
char PidChars[MAX_PID_SIZE];
|
||||
char Hostname[COMPILER_RT_MAX_HOSTLEN];
|
||||
unsigned NumPids;
|
||||
unsigned NumHosts;
|
||||
/* When in-process merging is enabled, this parameter specifies
|
||||
* the total number of profile data files shared by all the processes
|
||||
* spawned from the same binary. By default the value is 1. If merging
|
||||
* is not enabled, its value should be 0. This parameter is specified
|
||||
* by the %[0-9]m specifier. For instance %2m enables merging using
|
||||
* 2 profile data files. %1m is equivalent to %m. Also %m specifier
|
||||
* can only appear once at the end of the name pattern. */
|
||||
unsigned MergePoolSize;
|
||||
ProfileNameSpecifier PNS;
|
||||
} lprofFilename;
|
||||
|
||||
COMPILER_RT_WEAK lprofFilename lprofCurFilename = {0, 0, 0, {0}, {0},
|
||||
0, 0, 0, PNS_unknown};
|
||||
|
||||
int getpid(void);
|
||||
static int getCurFilenameLength();
|
||||
static const char *getCurFilename(char *FilenameBuf);
|
||||
static unsigned doMerging() { return lprofCurFilename.MergePoolSize; }
|
||||
|
||||
/* Return 1 if there is an error, otherwise return 0. */
|
||||
static uint32_t fileWriter(ProfDataWriter *This, ProfDataIOVec *IOVecs,
|
||||
uint32_t NumIOVecs) {
|
||||
uint32_t I;
|
||||
FILE *File = (FILE *)This->WriterCtx;
|
||||
for (I = 0; I < NumIOVecs; I++) {
|
||||
if (IOVecs[I].Data) {
|
||||
if (fwrite(IOVecs[I].Data, IOVecs[I].ElmSize, IOVecs[I].NumElm, File) !=
|
||||
IOVecs[I].NumElm)
|
||||
return 1;
|
||||
} else {
|
||||
if (fseek(File, IOVecs[I].ElmSize * IOVecs[I].NumElm, SEEK_CUR) == -1)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void initFileWriter(ProfDataWriter *This, FILE *File) {
|
||||
This->Write = fileWriter;
|
||||
This->WriterCtx = File;
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY ProfBufferIO *
|
||||
lprofCreateBufferIOInternal(void *File, uint32_t BufferSz) {
|
||||
FreeHook = &free;
|
||||
DynamicBufferIOBuffer = (uint8_t *)calloc(BufferSz, 1);
|
||||
VPBufferSize = BufferSz;
|
||||
ProfDataWriter *fileWriter =
|
||||
(ProfDataWriter *)calloc(sizeof(ProfDataWriter), 1);
|
||||
initFileWriter(fileWriter, File);
|
||||
ProfBufferIO *IO = lprofCreateBufferIO(fileWriter);
|
||||
IO->OwnFileWriter = 1;
|
||||
return IO;
|
||||
}
|
||||
|
||||
static void setupIOBuffer() {
|
||||
const char *BufferSzStr = 0;
|
||||
BufferSzStr = getenv("LLVM_VP_BUFFER_SIZE");
|
||||
if (BufferSzStr && BufferSzStr[0]) {
|
||||
VPBufferSize = atoi(BufferSzStr);
|
||||
DynamicBufferIOBuffer = (uint8_t *)calloc(VPBufferSize, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Read profile data in \c ProfileFile and merge with in-memory
|
||||
profile counters. Returns -1 if there is fatal error, otheriwse
|
||||
0 is returned. Returning 0 does not mean merge is actually
|
||||
performed. If merge is actually done, *MergeDone is set to 1.
|
||||
*/
|
||||
static int doProfileMerging(FILE *ProfileFile, int *MergeDone) {
|
||||
uint64_t ProfileFileSize;
|
||||
char *ProfileBuffer;
|
||||
|
||||
if (fseek(ProfileFile, 0L, SEEK_END) == -1) {
|
||||
PROF_ERR("Unable to merge profile data, unable to get size: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
ProfileFileSize = ftell(ProfileFile);
|
||||
|
||||
/* Restore file offset. */
|
||||
if (fseek(ProfileFile, 0L, SEEK_SET) == -1) {
|
||||
PROF_ERR("Unable to merge profile data, unable to rewind: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Nothing to merge. */
|
||||
if (ProfileFileSize < sizeof(__llvm_profile_header)) {
|
||||
if (ProfileFileSize)
|
||||
PROF_WARN("Unable to merge profile data: %s\n",
|
||||
"source profile file is too small.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ProfileBuffer = mmap(NULL, ProfileFileSize, PROT_READ, MAP_SHARED | MAP_FILE,
|
||||
fileno(ProfileFile), 0);
|
||||
if (ProfileBuffer == MAP_FAILED) {
|
||||
PROF_ERR("Unable to merge profile data, mmap failed: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (__llvm_profile_check_compatibility(ProfileBuffer, ProfileFileSize)) {
|
||||
(void)munmap(ProfileBuffer, ProfileFileSize);
|
||||
PROF_WARN("Unable to merge profile data: %s\n",
|
||||
"source profile file is not compatible.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Now start merging */
|
||||
__llvm_profile_merge_from_buffer(ProfileBuffer, ProfileFileSize);
|
||||
(void)munmap(ProfileBuffer, ProfileFileSize);
|
||||
|
||||
*MergeDone = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create the directory holding the file, if needed. */
|
||||
static void createProfileDir(const char *Filename) {
|
||||
size_t Length = strlen(Filename);
|
||||
if (lprofFindFirstDirSeparator(Filename)) {
|
||||
char *Copy = (char *)COMPILER_RT_ALLOCA(Length + 1);
|
||||
strncpy(Copy, Filename, Length + 1);
|
||||
__llvm_profile_recursive_mkdir(Copy);
|
||||
}
|
||||
}
|
||||
|
||||
/* Open the profile data for merging. It opens the file in r+b mode with
|
||||
* file locking. If the file has content which is compatible with the
|
||||
* current process, it also reads in the profile data in the file and merge
|
||||
* it with in-memory counters. After the profile data is merged in memory,
|
||||
* the original profile data is truncated and gets ready for the profile
|
||||
* dumper. With profile merging enabled, each executable as well as any of
|
||||
* its instrumented shared libraries dump profile data into their own data file.
|
||||
*/
|
||||
static FILE *openFileForMerging(const char *ProfileFileName, int *MergeDone) {
|
||||
FILE *ProfileFile;
|
||||
int rc;
|
||||
|
||||
createProfileDir(ProfileFileName);
|
||||
ProfileFile = lprofOpenFileEx(ProfileFileName);
|
||||
if (!ProfileFile)
|
||||
return NULL;
|
||||
|
||||
rc = doProfileMerging(ProfileFile, MergeDone);
|
||||
if (rc || (!*MergeDone && COMPILER_RT_FTRUNCATE(ProfileFile, 0L)) ||
|
||||
fseek(ProfileFile, 0L, SEEK_SET) == -1) {
|
||||
PROF_ERR("Profile Merging of file %s failed: %s\n", ProfileFileName,
|
||||
strerror(errno));
|
||||
fclose(ProfileFile);
|
||||
return NULL;
|
||||
}
|
||||
return ProfileFile;
|
||||
}
|
||||
|
||||
/* Write profile data to file \c OutputName. */
|
||||
static int writeFile(const char *OutputName) {
|
||||
int RetVal;
|
||||
FILE *OutputFile;
|
||||
|
||||
int MergeDone = 0;
|
||||
if (!doMerging())
|
||||
OutputFile = fopen(OutputName, "ab");
|
||||
else
|
||||
OutputFile = openFileForMerging(OutputName, &MergeDone);
|
||||
|
||||
if (!OutputFile)
|
||||
return -1;
|
||||
|
||||
FreeHook = &free;
|
||||
setupIOBuffer();
|
||||
ProfDataWriter fileWriter;
|
||||
initFileWriter(&fileWriter, OutputFile);
|
||||
RetVal = lprofWriteData(&fileWriter, lprofGetVPDataReader(), MergeDone);
|
||||
|
||||
fclose(OutputFile);
|
||||
return RetVal;
|
||||
}
|
||||
|
||||
static void truncateCurrentFile(void) {
|
||||
const char *Filename;
|
||||
char *FilenameBuf;
|
||||
FILE *File;
|
||||
int Length;
|
||||
|
||||
Length = getCurFilenameLength();
|
||||
FilenameBuf = (char *)COMPILER_RT_ALLOCA(Length + 1);
|
||||
Filename = getCurFilename(FilenameBuf);
|
||||
if (!Filename)
|
||||
return;
|
||||
|
||||
/* By pass file truncation to allow online raw profile
|
||||
* merging. */
|
||||
if (lprofCurFilename.MergePoolSize)
|
||||
return;
|
||||
|
||||
createProfileDir(Filename);
|
||||
|
||||
/* Truncate the file. Later we'll reopen and append. */
|
||||
File = fopen(Filename, "w");
|
||||
if (!File)
|
||||
return;
|
||||
fclose(File);
|
||||
}
|
||||
|
||||
static const char *DefaultProfileName = "default.profraw";
|
||||
static void resetFilenameToDefault(void) {
|
||||
if (lprofCurFilename.FilenamePat && lprofCurFilename.OwnsFilenamePat) {
|
||||
free((void *)lprofCurFilename.FilenamePat);
|
||||
}
|
||||
memset(&lprofCurFilename, 0, sizeof(lprofCurFilename));
|
||||
lprofCurFilename.FilenamePat = DefaultProfileName;
|
||||
lprofCurFilename.PNS = PNS_default;
|
||||
}
|
||||
|
||||
static int containsMergeSpecifier(const char *FilenamePat, int I) {
|
||||
return (FilenamePat[I] == 'm' ||
|
||||
(FilenamePat[I] >= '1' && FilenamePat[I] <= '9' &&
|
||||
/* If FilenamePat[I] is not '\0', the next byte is guaranteed
|
||||
* to be in-bound as the string is null terminated. */
|
||||
FilenamePat[I + 1] == 'm'));
|
||||
}
|
||||
|
||||
/* Parses the pattern string \p FilenamePat and stores the result to
|
||||
* lprofcurFilename structure. */
|
||||
static int parseFilenamePattern(const char *FilenamePat,
|
||||
unsigned CopyFilenamePat) {
|
||||
int NumPids = 0, NumHosts = 0, I;
|
||||
char *PidChars = &lprofCurFilename.PidChars[0];
|
||||
char *Hostname = &lprofCurFilename.Hostname[0];
|
||||
int MergingEnabled = 0;
|
||||
|
||||
/* Clean up cached prefix. */
|
||||
if (lprofCurFilename.ProfilePathPrefix)
|
||||
free((void *)lprofCurFilename.ProfilePathPrefix);
|
||||
memset(&lprofCurFilename, 0, sizeof(lprofCurFilename));
|
||||
|
||||
if (lprofCurFilename.FilenamePat && lprofCurFilename.OwnsFilenamePat) {
|
||||
free((void *)lprofCurFilename.FilenamePat);
|
||||
}
|
||||
|
||||
if (!CopyFilenamePat)
|
||||
lprofCurFilename.FilenamePat = FilenamePat;
|
||||
else {
|
||||
lprofCurFilename.FilenamePat = strdup(FilenamePat);
|
||||
lprofCurFilename.OwnsFilenamePat = 1;
|
||||
}
|
||||
/* Check the filename for "%p", which indicates a pid-substitution. */
|
||||
for (I = 0; FilenamePat[I]; ++I)
|
||||
if (FilenamePat[I] == '%') {
|
||||
if (FilenamePat[++I] == 'p') {
|
||||
if (!NumPids++) {
|
||||
if (snprintf(PidChars, MAX_PID_SIZE, "%d", getpid()) <= 0) {
|
||||
PROF_WARN("Unable to get pid for filename pattern %s. Using the "
|
||||
"default name.",
|
||||
FilenamePat);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} else if (FilenamePat[I] == 'h') {
|
||||
if (!NumHosts++)
|
||||
if (COMPILER_RT_GETHOSTNAME(Hostname, COMPILER_RT_MAX_HOSTLEN)) {
|
||||
PROF_WARN("Unable to get hostname for filename pattern %s. Using "
|
||||
"the default name.",
|
||||
FilenamePat);
|
||||
return -1;
|
||||
}
|
||||
} else if (containsMergeSpecifier(FilenamePat, I)) {
|
||||
if (MergingEnabled) {
|
||||
PROF_WARN("%%m specifier can only be specified once in %s.\n",
|
||||
FilenamePat);
|
||||
return -1;
|
||||
}
|
||||
MergingEnabled = 1;
|
||||
if (FilenamePat[I] == 'm')
|
||||
lprofCurFilename.MergePoolSize = 1;
|
||||
else {
|
||||
lprofCurFilename.MergePoolSize = FilenamePat[I] - '0';
|
||||
I++; /* advance to 'm' */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lprofCurFilename.NumPids = NumPids;
|
||||
lprofCurFilename.NumHosts = NumHosts;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void parseAndSetFilename(const char *FilenamePat,
|
||||
ProfileNameSpecifier PNS,
|
||||
unsigned CopyFilenamePat) {
|
||||
|
||||
const char *OldFilenamePat = lprofCurFilename.FilenamePat;
|
||||
ProfileNameSpecifier OldPNS = lprofCurFilename.PNS;
|
||||
|
||||
if (PNS < OldPNS)
|
||||
return;
|
||||
|
||||
if (!FilenamePat)
|
||||
FilenamePat = DefaultProfileName;
|
||||
|
||||
if (OldFilenamePat && !strcmp(OldFilenamePat, FilenamePat)) {
|
||||
lprofCurFilename.PNS = PNS;
|
||||
return;
|
||||
}
|
||||
|
||||
/* When PNS >= OldPNS, the last one wins. */
|
||||
if (!FilenamePat || parseFilenamePattern(FilenamePat, CopyFilenamePat))
|
||||
resetFilenameToDefault();
|
||||
lprofCurFilename.PNS = PNS;
|
||||
|
||||
if (!OldFilenamePat) {
|
||||
if (getenv("LLVM_PROFILE_VERBOSE"))
|
||||
PROF_NOTE("Set profile file path to \"%s\" via %s.\n",
|
||||
lprofCurFilename.FilenamePat, getPNSStr(PNS));
|
||||
} else {
|
||||
if (getenv("LLVM_PROFILE_VERBOSE"))
|
||||
PROF_NOTE("Override old profile path \"%s\" via %s to \"%s\" via %s.\n",
|
||||
OldFilenamePat, getPNSStr(OldPNS), lprofCurFilename.FilenamePat,
|
||||
getPNSStr(PNS));
|
||||
}
|
||||
|
||||
truncateCurrentFile();
|
||||
}
|
||||
|
||||
/* Return buffer length that is required to store the current profile
|
||||
* filename with PID and hostname substitutions. */
|
||||
/* The length to hold uint64_t followed by 2 digit pool id including '_' */
|
||||
#define SIGLEN 24
|
||||
static int getCurFilenameLength() {
|
||||
int Len;
|
||||
if (!lprofCurFilename.FilenamePat || !lprofCurFilename.FilenamePat[0])
|
||||
return 0;
|
||||
|
||||
if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts ||
|
||||
lprofCurFilename.MergePoolSize))
|
||||
return strlen(lprofCurFilename.FilenamePat);
|
||||
|
||||
Len = strlen(lprofCurFilename.FilenamePat) +
|
||||
lprofCurFilename.NumPids * (strlen(lprofCurFilename.PidChars) - 2) +
|
||||
lprofCurFilename.NumHosts * (strlen(lprofCurFilename.Hostname) - 2);
|
||||
if (lprofCurFilename.MergePoolSize)
|
||||
Len += SIGLEN;
|
||||
return Len;
|
||||
}
|
||||
|
||||
/* Return the pointer to the current profile file name (after substituting
|
||||
* PIDs and Hostnames in filename pattern. \p FilenameBuf is the buffer
|
||||
* to store the resulting filename. If no substitution is needed, the
|
||||
* current filename pattern string is directly returned. */
|
||||
static const char *getCurFilename(char *FilenameBuf) {
|
||||
int I, J, PidLength, HostNameLength;
|
||||
const char *FilenamePat = lprofCurFilename.FilenamePat;
|
||||
|
||||
if (!lprofCurFilename.FilenamePat || !lprofCurFilename.FilenamePat[0])
|
||||
return 0;
|
||||
|
||||
if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts ||
|
||||
lprofCurFilename.MergePoolSize))
|
||||
return lprofCurFilename.FilenamePat;
|
||||
|
||||
PidLength = strlen(lprofCurFilename.PidChars);
|
||||
HostNameLength = strlen(lprofCurFilename.Hostname);
|
||||
/* Construct the new filename. */
|
||||
for (I = 0, J = 0; FilenamePat[I]; ++I)
|
||||
if (FilenamePat[I] == '%') {
|
||||
if (FilenamePat[++I] == 'p') {
|
||||
memcpy(FilenameBuf + J, lprofCurFilename.PidChars, PidLength);
|
||||
J += PidLength;
|
||||
} else if (FilenamePat[I] == 'h') {
|
||||
memcpy(FilenameBuf + J, lprofCurFilename.Hostname, HostNameLength);
|
||||
J += HostNameLength;
|
||||
} else if (containsMergeSpecifier(FilenamePat, I)) {
|
||||
char LoadModuleSignature[SIGLEN];
|
||||
int S;
|
||||
int ProfilePoolId = getpid() % lprofCurFilename.MergePoolSize;
|
||||
S = snprintf(LoadModuleSignature, SIGLEN, "%" PRIu64 "_%d",
|
||||
lprofGetLoadModuleSignature(), ProfilePoolId);
|
||||
if (S == -1 || S > SIGLEN)
|
||||
S = SIGLEN;
|
||||
memcpy(FilenameBuf + J, LoadModuleSignature, S);
|
||||
J += S;
|
||||
if (FilenamePat[I] != 'm')
|
||||
I++;
|
||||
}
|
||||
/* Drop any unknown substitutions. */
|
||||
} else
|
||||
FilenameBuf[J++] = FilenamePat[I];
|
||||
FilenameBuf[J] = 0;
|
||||
|
||||
return FilenameBuf;
|
||||
}
|
||||
|
||||
/* Returns the pointer to the environment variable
|
||||
* string. Returns null if the env var is not set. */
|
||||
static const char *getFilenamePatFromEnv(void) {
|
||||
const char *Filename = getenv("LLVM_PROFILE_FILE");
|
||||
if (!Filename || !Filename[0])
|
||||
return 0;
|
||||
return Filename;
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY
|
||||
const char *__llvm_profile_get_path_prefix(void) {
|
||||
int Length;
|
||||
char *FilenameBuf, *Prefix;
|
||||
const char *Filename, *PrefixEnd;
|
||||
|
||||
if (lprofCurFilename.ProfilePathPrefix)
|
||||
return lprofCurFilename.ProfilePathPrefix;
|
||||
|
||||
Length = getCurFilenameLength();
|
||||
FilenameBuf = (char *)COMPILER_RT_ALLOCA(Length + 1);
|
||||
Filename = getCurFilename(FilenameBuf);
|
||||
if (!Filename)
|
||||
return "\0";
|
||||
|
||||
PrefixEnd = lprofFindLastDirSeparator(Filename);
|
||||
if (!PrefixEnd)
|
||||
return "\0";
|
||||
|
||||
Length = PrefixEnd - Filename + 1;
|
||||
Prefix = (char *)malloc(Length + 1);
|
||||
if (!Prefix) {
|
||||
PROF_ERR("Failed to %s\n", "allocate memory.");
|
||||
return "\0";
|
||||
}
|
||||
memcpy(Prefix, Filename, Length);
|
||||
Prefix[Length] = '\0';
|
||||
lprofCurFilename.ProfilePathPrefix = Prefix;
|
||||
return Prefix;
|
||||
}
|
||||
|
||||
/* This method is invoked by the runtime initialization hook
|
||||
* InstrProfilingRuntime.o if it is linked in. Both user specified
|
||||
* profile path via -fprofile-instr-generate= and LLVM_PROFILE_FILE
|
||||
* environment variable can override this default value. */
|
||||
COMPILER_RT_VISIBILITY
|
||||
void __llvm_profile_initialize_file(void) {
|
||||
const char *EnvFilenamePat;
|
||||
const char *SelectedPat = NULL;
|
||||
ProfileNameSpecifier PNS = PNS_unknown;
|
||||
int hasCommandLineOverrider = (INSTR_PROF_PROFILE_NAME_VAR[0] != 0);
|
||||
|
||||
EnvFilenamePat = getFilenamePatFromEnv();
|
||||
if (EnvFilenamePat) {
|
||||
/* Pass CopyFilenamePat = 1, to ensure that the filename would be valid
|
||||
at the moment when __llvm_profile_write_file() gets executed. */
|
||||
parseAndSetFilename(EnvFilenamePat, PNS_environment, 1);
|
||||
return;
|
||||
} else if (hasCommandLineOverrider) {
|
||||
SelectedPat = INSTR_PROF_PROFILE_NAME_VAR;
|
||||
PNS = PNS_command_line;
|
||||
} else {
|
||||
SelectedPat = NULL;
|
||||
PNS = PNS_default;
|
||||
}
|
||||
|
||||
parseAndSetFilename(SelectedPat, PNS, 0);
|
||||
}
|
||||
|
||||
/* This API is directly called by the user application code. It has the
|
||||
* highest precedence compared with LLVM_PROFILE_FILE environment variable
|
||||
* and command line option -fprofile-instr-generate=<profile_name>.
|
||||
*/
|
||||
COMPILER_RT_VISIBILITY
|
||||
void __llvm_profile_set_filename(const char *FilenamePat) {
|
||||
parseAndSetFilename(FilenamePat, PNS_runtime_api, 1);
|
||||
}
|
||||
|
||||
/* The public API for writing profile data into the file with name
|
||||
* set by previous calls to __llvm_profile_set_filename or
|
||||
* __llvm_profile_override_default_filename or
|
||||
* __llvm_profile_initialize_file. */
|
||||
COMPILER_RT_VISIBILITY
|
||||
int __llvm_profile_write_file(void) {
|
||||
int rc, Length;
|
||||
const char *Filename;
|
||||
char *FilenameBuf;
|
||||
int PDeathSig = 0;
|
||||
|
||||
if (lprofProfileDumped()) {
|
||||
PROF_NOTE("Profile data not written to file: %s.\n",
|
||||
"already written");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Length = getCurFilenameLength();
|
||||
FilenameBuf = (char *)COMPILER_RT_ALLOCA(Length + 1);
|
||||
Filename = getCurFilename(FilenameBuf);
|
||||
|
||||
/* Check the filename. */
|
||||
if (!Filename) {
|
||||
PROF_ERR("Failed to write file : %s\n", "Filename not set");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check if there is llvm/runtime version mismatch. */
|
||||
if (GET_VERSION(__llvm_profile_get_version()) != INSTR_PROF_RAW_VERSION) {
|
||||
PROF_ERR("Runtime and instrumentation version mismatch : "
|
||||
"expected %d, but get %d\n",
|
||||
INSTR_PROF_RAW_VERSION,
|
||||
(int)GET_VERSION(__llvm_profile_get_version()));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Temporarily suspend getting SIGKILL when the parent exits.
|
||||
PDeathSig = lprofSuspendSigKill();
|
||||
|
||||
/* Write profile data to the file. */
|
||||
rc = writeFile(Filename);
|
||||
if (rc)
|
||||
PROF_ERR("Failed to write file \"%s\": %s\n", Filename, strerror(errno));
|
||||
|
||||
// Restore SIGKILL.
|
||||
if (PDeathSig == 1)
|
||||
lprofRestoreSigKill();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY
|
||||
int __llvm_profile_dump(void) {
|
||||
if (!doMerging())
|
||||
PROF_WARN("Later invocation of __llvm_profile_dump can lead to clobbering "
|
||||
" of previously dumped profile data : %s. Either use %%m "
|
||||
"in profile name or change profile name before dumping.\n",
|
||||
"online profile merging is not on");
|
||||
int rc = __llvm_profile_write_file();
|
||||
lprofSetProfileDumped();
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void writeFileWithoutReturn(void) { __llvm_profile_write_file(); }
|
||||
|
||||
COMPILER_RT_VISIBILITY
|
||||
int __llvm_profile_register_write_file_atexit(void) {
|
||||
static int HasBeenRegistered = 0;
|
||||
|
||||
if (HasBeenRegistered)
|
||||
return 0;
|
||||
|
||||
lprofSetupValueProfiler();
|
||||
|
||||
HasBeenRegistered = 1;
|
||||
return atexit(writeFileWithoutReturn);
|
||||
}
|
189
runtime/profile-rt/profile-rt-70/InstrProfilingInternal.h
Normal file
189
runtime/profile-rt/profile-rt-70/InstrProfilingInternal.h
Normal file
|
@ -0,0 +1,189 @@
|
|||
/*===- InstrProfiling.h- Support library for PGO instrumentation ----------===*\
|
||||
|*
|
||||
|* The LLVM Compiler Infrastructure
|
||||
|*
|
||||
|* This file is distributed under the University of Illinois Open Source
|
||||
|* License. See LICENSE.TXT for details.
|
||||
|*
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
#ifndef PROFILE_INSTRPROFILING_INTERNALH_
|
||||
#define PROFILE_INSTRPROFILING_INTERNALH_
|
||||
|
||||
#include "InstrProfiling.h"
|
||||
#include "stddef.h"
|
||||
|
||||
/*!
|
||||
* \brief Write instrumentation data to the given buffer, given explicit
|
||||
* pointers to the live data in memory. This function is probably not what you
|
||||
* want. Use __llvm_profile_get_size_for_buffer instead. Use this function if
|
||||
* your program has a custom memory layout.
|
||||
*/
|
||||
uint64_t __llvm_profile_get_size_for_buffer_internal(
|
||||
const __llvm_profile_data *DataBegin, const __llvm_profile_data *DataEnd,
|
||||
const uint64_t *CountersBegin, const uint64_t *CountersEnd,
|
||||
const char *NamesBegin, const char *NamesEnd);
|
||||
|
||||
/*!
|
||||
* \brief Write instrumentation data to the given buffer, given explicit
|
||||
* pointers to the live data in memory. This function is probably not what you
|
||||
* want. Use __llvm_profile_write_buffer instead. Use this function if your
|
||||
* program has a custom memory layout.
|
||||
*
|
||||
* \pre \c Buffer is the start of a buffer at least as big as \a
|
||||
* __llvm_profile_get_size_for_buffer_internal().
|
||||
*/
|
||||
int __llvm_profile_write_buffer_internal(
|
||||
char *Buffer, const __llvm_profile_data *DataBegin,
|
||||
const __llvm_profile_data *DataEnd, const uint64_t *CountersBegin,
|
||||
const uint64_t *CountersEnd, const char *NamesBegin, const char *NamesEnd);
|
||||
|
||||
/*!
|
||||
* The data structure describing the data to be written by the
|
||||
* low level writer callback function.
|
||||
*/
|
||||
typedef struct ProfDataIOVec {
|
||||
const void *Data;
|
||||
size_t ElmSize;
|
||||
size_t NumElm;
|
||||
} ProfDataIOVec;
|
||||
|
||||
struct ProfDataWriter;
|
||||
typedef uint32_t (*WriterCallback)(struct ProfDataWriter *This, ProfDataIOVec *,
|
||||
uint32_t NumIOVecs);
|
||||
|
||||
typedef struct ProfDataWriter {
|
||||
WriterCallback Write;
|
||||
void *WriterCtx;
|
||||
} ProfDataWriter;
|
||||
|
||||
/*!
|
||||
* The data structure for buffered IO of profile data.
|
||||
*/
|
||||
typedef struct ProfBufferIO {
|
||||
ProfDataWriter *FileWriter;
|
||||
uint32_t OwnFileWriter;
|
||||
/* The start of the buffer. */
|
||||
uint8_t *BufferStart;
|
||||
/* Total size of the buffer. */
|
||||
uint32_t BufferSz;
|
||||
/* Current byte offset from the start of the buffer. */
|
||||
uint32_t CurOffset;
|
||||
} ProfBufferIO;
|
||||
|
||||
/* The creator interface used by testing. */
|
||||
ProfBufferIO *lprofCreateBufferIOInternal(void *File, uint32_t BufferSz);
|
||||
|
||||
/*!
|
||||
* This is the interface to create a handle for buffered IO.
|
||||
*/
|
||||
ProfBufferIO *lprofCreateBufferIO(ProfDataWriter *FileWriter);
|
||||
|
||||
/*!
|
||||
* The interface to destroy the bufferIO handle and reclaim
|
||||
* the memory.
|
||||
*/
|
||||
void lprofDeleteBufferIO(ProfBufferIO *BufferIO);
|
||||
|
||||
/*!
|
||||
* This is the interface to write \c Data of \c Size bytes through
|
||||
* \c BufferIO. Returns 0 if successful, otherwise return -1.
|
||||
*/
|
||||
int lprofBufferIOWrite(ProfBufferIO *BufferIO, const uint8_t *Data,
|
||||
uint32_t Size);
|
||||
/*!
|
||||
* The interface to flush the remaining data in the buffer.
|
||||
* through the low level writer callback.
|
||||
*/
|
||||
int lprofBufferIOFlush(ProfBufferIO *BufferIO);
|
||||
|
||||
/* The low level interface to write data into a buffer. It is used as the
|
||||
* callback by other high level writer methods such as buffered IO writer
|
||||
* and profile data writer. */
|
||||
uint32_t lprofBufferWriter(ProfDataWriter *This, ProfDataIOVec *IOVecs,
|
||||
uint32_t NumIOVecs);
|
||||
void initBufferWriter(ProfDataWriter *BufferWriter, char *Buffer);
|
||||
|
||||
struct ValueProfData;
|
||||
struct ValueProfRecord;
|
||||
struct InstrProfValueData;
|
||||
struct ValueProfNode;
|
||||
|
||||
/*!
|
||||
* The class that defines a set of methods to read value profile
|
||||
* data for streaming/serialization from the instrumentation runtime.
|
||||
*/
|
||||
typedef struct VPDataReaderType {
|
||||
uint32_t (*InitRTRecord)(const __llvm_profile_data *Data,
|
||||
uint8_t *SiteCountArray[]);
|
||||
/* Function pointer to getValueProfRecordHeader method. */
|
||||
uint32_t (*GetValueProfRecordHeaderSize)(uint32_t NumSites);
|
||||
/* Function pointer to getFristValueProfRecord method. */
|
||||
struct ValueProfRecord *(*GetFirstValueProfRecord)(struct ValueProfData *);
|
||||
/* Return the number of value data for site \p Site. */
|
||||
uint32_t (*GetNumValueDataForSite)(uint32_t VK, uint32_t Site);
|
||||
/* Return the total size of the value profile data of the
|
||||
* current function. */
|
||||
uint32_t (*GetValueProfDataSize)(void);
|
||||
/*!
|
||||
* Read the next \p N value data for site \p Site and store the data
|
||||
* in \p Dst. \p StartNode is the first value node to start with if
|
||||
* it is not null. The function returns the pointer to the value
|
||||
* node pointer to be used as the \p StartNode of the next batch reading.
|
||||
* If there is nothing left, it returns NULL.
|
||||
*/
|
||||
struct ValueProfNode *(*GetValueData)(uint32_t ValueKind, uint32_t Site,
|
||||
struct InstrProfValueData *Dst,
|
||||
struct ValueProfNode *StartNode,
|
||||
uint32_t N);
|
||||
} VPDataReaderType;
|
||||
|
||||
/* Write profile data to destinitation. If SkipNameDataWrite is set to 1,
|
||||
the name data is already in destintation, we just skip over it. */
|
||||
int lprofWriteData(ProfDataWriter *Writer, VPDataReaderType *VPDataReader,
|
||||
int SkipNameDataWrite);
|
||||
int lprofWriteDataImpl(ProfDataWriter *Writer,
|
||||
const __llvm_profile_data *DataBegin,
|
||||
const __llvm_profile_data *DataEnd,
|
||||
const uint64_t *CountersBegin,
|
||||
const uint64_t *CountersEnd,
|
||||
VPDataReaderType *VPDataReader, const char *NamesBegin,
|
||||
const char *NamesEnd, int SkipNameDataWrite);
|
||||
|
||||
/* Merge value profile data pointed to by SrcValueProfData into
|
||||
* in-memory profile counters pointed by to DstData. */
|
||||
void lprofMergeValueProfData(struct ValueProfData *SrcValueProfData,
|
||||
__llvm_profile_data *DstData);
|
||||
|
||||
VPDataReaderType *lprofGetVPDataReader();
|
||||
|
||||
/* Internal interface used by test to reset the max number of
|
||||
* tracked values per value site to be \p MaxVals.
|
||||
*/
|
||||
void lprofSetMaxValsPerSite(uint32_t MaxVals);
|
||||
void lprofSetupValueProfiler();
|
||||
|
||||
/* Return the profile header 'signature' value associated with the current
|
||||
* executable or shared library. The signature value can be used to for
|
||||
* a profile name that is unique to this load module so that it does not
|
||||
* collide with profiles from other binaries. It also allows shared libraries
|
||||
* to dump merged profile data into its own profile file. */
|
||||
uint64_t lprofGetLoadModuleSignature();
|
||||
|
||||
/*
|
||||
* Return non zero value if the profile data has already been
|
||||
* dumped to the file.
|
||||
*/
|
||||
unsigned lprofProfileDumped();
|
||||
void lprofSetProfileDumped();
|
||||
|
||||
COMPILER_RT_VISIBILITY extern void (*FreeHook)(void *);
|
||||
COMPILER_RT_VISIBILITY extern uint8_t *DynamicBufferIOBuffer;
|
||||
COMPILER_RT_VISIBILITY extern uint32_t VPBufferSize;
|
||||
COMPILER_RT_VISIBILITY extern uint32_t VPMaxNumValsPerSite;
|
||||
/* Pointer to the start of static value counters to be allocted. */
|
||||
COMPILER_RT_VISIBILITY extern ValueProfNode *CurrentVNode;
|
||||
COMPILER_RT_VISIBILITY extern ValueProfNode *EndVNode;
|
||||
extern void (*VPMergeHook)(struct ValueProfData *, __llvm_profile_data *);
|
||||
|
||||
#endif
|
132
runtime/profile-rt/profile-rt-70/InstrProfilingMerge.c
Normal file
132
runtime/profile-rt/profile-rt-70/InstrProfilingMerge.c
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*===- InstrProfilingMerge.c - Profile in-process Merging ---------------===*\
|
||||
|*
|
||||
|* The LLVM Compiler Infrastructure
|
||||
|*
|
||||
|* This file is distributed under the University of Illinois Open Source
|
||||
|* License. See LICENSE.TXT for details.
|
||||
|*
|
||||
|*===----------------------------------------------------------------------===*
|
||||
|* This file defines the API needed for in-process merging of profile data
|
||||
|* stored in memory buffer.
|
||||
\*===---------------------------------------------------------------------===*/
|
||||
|
||||
#include "InstrProfiling.h"
|
||||
#include "InstrProfilingInternal.h"
|
||||
#include "InstrProfilingUtil.h"
|
||||
|
||||
#define INSTR_PROF_VALUE_PROF_DATA
|
||||
#include "InstrProfData.inc"
|
||||
|
||||
COMPILER_RT_WEAK void (*VPMergeHook)(ValueProfData *,
|
||||
__llvm_profile_data *) = NULL;
|
||||
COMPILER_RT_VISIBILITY
|
||||
uint64_t lprofGetLoadModuleSignature() {
|
||||
/* A very fast way to compute a module signature. */
|
||||
uint64_t CounterSize = (uint64_t)(__llvm_profile_end_counters() -
|
||||
__llvm_profile_begin_counters());
|
||||
uint64_t DataSize = __llvm_profile_get_data_size(__llvm_profile_begin_data(),
|
||||
__llvm_profile_end_data());
|
||||
uint64_t NamesSize =
|
||||
(uint64_t)(__llvm_profile_end_names() - __llvm_profile_begin_names());
|
||||
uint64_t NumVnodes =
|
||||
(uint64_t)(__llvm_profile_end_vnodes() - __llvm_profile_begin_vnodes());
|
||||
const __llvm_profile_data *FirstD = __llvm_profile_begin_data();
|
||||
|
||||
return (NamesSize << 40) + (CounterSize << 30) + (DataSize << 20) +
|
||||
(NumVnodes << 10) + (DataSize > 0 ? FirstD->NameRef : 0);
|
||||
}
|
||||
|
||||
/* Returns 1 if profile is not structurally compatible. */
|
||||
COMPILER_RT_VISIBILITY
|
||||
int __llvm_profile_check_compatibility(const char *ProfileData,
|
||||
uint64_t ProfileSize) {
|
||||
/* Check profile header only for now */
|
||||
__llvm_profile_header *Header = (__llvm_profile_header *)ProfileData;
|
||||
__llvm_profile_data *SrcDataStart, *SrcDataEnd, *SrcData, *DstData;
|
||||
SrcDataStart =
|
||||
(__llvm_profile_data *)(ProfileData + sizeof(__llvm_profile_header));
|
||||
SrcDataEnd = SrcDataStart + Header->DataSize;
|
||||
|
||||
if (ProfileSize < sizeof(__llvm_profile_header))
|
||||
return 1;
|
||||
|
||||
/* Check the header first. */
|
||||
if (Header->Magic != __llvm_profile_get_magic() ||
|
||||
Header->Version != __llvm_profile_get_version() ||
|
||||
Header->DataSize !=
|
||||
__llvm_profile_get_data_size(__llvm_profile_begin_data(),
|
||||
__llvm_profile_end_data()) ||
|
||||
Header->CountersSize != (uint64_t)(__llvm_profile_end_counters() -
|
||||
__llvm_profile_begin_counters()) ||
|
||||
Header->NamesSize != (uint64_t)(__llvm_profile_end_names() -
|
||||
__llvm_profile_begin_names()) ||
|
||||
Header->ValueKindLast != IPVK_Last)
|
||||
return 1;
|
||||
|
||||
if (ProfileSize < sizeof(__llvm_profile_header) +
|
||||
Header->DataSize * sizeof(__llvm_profile_data) +
|
||||
Header->NamesSize + Header->CountersSize)
|
||||
return 1;
|
||||
|
||||
for (SrcData = SrcDataStart,
|
||||
DstData = (__llvm_profile_data *)__llvm_profile_begin_data();
|
||||
SrcData < SrcDataEnd; ++SrcData, ++DstData) {
|
||||
if (SrcData->NameRef != DstData->NameRef ||
|
||||
SrcData->FuncHash != DstData->FuncHash ||
|
||||
SrcData->NumCounters != DstData->NumCounters)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Matched! */
|
||||
return 0;
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY
|
||||
void __llvm_profile_merge_from_buffer(const char *ProfileData,
|
||||
uint64_t ProfileSize) {
|
||||
__llvm_profile_data *SrcDataStart, *SrcDataEnd, *SrcData, *DstData;
|
||||
__llvm_profile_header *Header = (__llvm_profile_header *)ProfileData;
|
||||
uint64_t *SrcCountersStart;
|
||||
const char *SrcNameStart;
|
||||
ValueProfData *SrcValueProfDataStart, *SrcValueProfData;
|
||||
|
||||
SrcDataStart =
|
||||
(__llvm_profile_data *)(ProfileData + sizeof(__llvm_profile_header));
|
||||
SrcDataEnd = SrcDataStart + Header->DataSize;
|
||||
SrcCountersStart = (uint64_t *)SrcDataEnd;
|
||||
SrcNameStart = (const char *)(SrcCountersStart + Header->CountersSize);
|
||||
SrcValueProfDataStart =
|
||||
(ValueProfData *)(SrcNameStart + Header->NamesSize +
|
||||
__llvm_profile_get_num_padding_bytes(
|
||||
Header->NamesSize));
|
||||
|
||||
for (SrcData = SrcDataStart,
|
||||
DstData = (__llvm_profile_data *)__llvm_profile_begin_data(),
|
||||
SrcValueProfData = SrcValueProfDataStart;
|
||||
SrcData < SrcDataEnd; ++SrcData, ++DstData) {
|
||||
uint64_t *SrcCounters;
|
||||
uint64_t *DstCounters = (uint64_t *)DstData->CounterPtr;
|
||||
unsigned I, NC, NVK = 0;
|
||||
|
||||
NC = SrcData->NumCounters;
|
||||
SrcCounters = SrcCountersStart +
|
||||
((size_t)SrcData->CounterPtr - Header->CountersDelta) /
|
||||
sizeof(uint64_t);
|
||||
for (I = 0; I < NC; I++)
|
||||
DstCounters[I] += SrcCounters[I];
|
||||
|
||||
/* Now merge value profile data. */
|
||||
if (!VPMergeHook)
|
||||
continue;
|
||||
|
||||
for (I = 0; I <= IPVK_Last; I++)
|
||||
NVK += (SrcData->NumValueSites[I] != 0);
|
||||
|
||||
if (!NVK)
|
||||
continue;
|
||||
|
||||
VPMergeHook(SrcValueProfData, DstData);
|
||||
SrcValueProfData = (ValueProfData *)((char *)SrcValueProfData +
|
||||
SrcValueProfData->TotalSize);
|
||||
}
|
||||
}
|
41
runtime/profile-rt/profile-rt-70/InstrProfilingMergeFile.c
Normal file
41
runtime/profile-rt/profile-rt-70/InstrProfilingMergeFile.c
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*===- InstrProfilingMergeFile.c - Profile in-process Merging ------------===*\
|
||||
|*
|
||||
|* The LLVM Compiler Infrastructure
|
||||
|*
|
||||
|* This file is distributed under the University of Illinois Open Source
|
||||
|* License. See LICENSE.TXT for details.
|
||||
|*
|
||||
|*===----------------------------------------------------------------------===
|
||||
|* This file defines APIs needed to support in-process merging for profile data
|
||||
|* stored in files.
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
#include "InstrProfiling.h"
|
||||
#include "InstrProfilingInternal.h"
|
||||
#include "InstrProfilingUtil.h"
|
||||
|
||||
#define INSTR_PROF_VALUE_PROF_DATA
|
||||
#include "InstrProfData.inc"
|
||||
|
||||
void (*VPMergeHook)(ValueProfData *,
|
||||
__llvm_profile_data *) = &lprofMergeValueProfData;
|
||||
|
||||
/* Merge value profile data pointed to by SrcValueProfData into
|
||||
* in-memory profile counters pointed by to DstData. */
|
||||
void lprofMergeValueProfData(ValueProfData *SrcValueProfData,
|
||||
__llvm_profile_data *DstData) {
|
||||
unsigned I, S, V, C;
|
||||
InstrProfValueData *VData;
|
||||
ValueProfRecord *VR = getFirstValueProfRecord(SrcValueProfData);
|
||||
for (I = 0; I < SrcValueProfData->NumValueKinds; I++) {
|
||||
VData = getValueProfRecordValueData(VR);
|
||||
for (S = 0; S < VR->NumValueSites; S++) {
|
||||
uint8_t NV = VR->SiteCountArray[S];
|
||||
for (V = 0; V < NV; V++) {
|
||||
for (C = 0; C < VData[V].Count; C++)
|
||||
__llvm_profile_instrument_target(VData[V].Value, DstData, S);
|
||||
}
|
||||
}
|
||||
VR = getValueProfRecordNext(VR);
|
||||
}
|
||||
}
|
18
runtime/profile-rt/profile-rt-70/InstrProfilingNameVar.c
Normal file
18
runtime/profile-rt/profile-rt-70/InstrProfilingNameVar.c
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*===- InstrProfilingNameVar.c - profile name variable setup -------------===*\
|
||||
|*
|
||||
|* The LLVM Compiler Infrastructure
|
||||
|*
|
||||
|* This file is distributed under the University of Illinois Open Source
|
||||
|* License. See LICENSE.TXT for details.
|
||||
|*
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
#include "InstrProfiling.h"
|
||||
|
||||
/* char __llvm_profile_filename[1]
|
||||
*
|
||||
* The runtime should only provide its own definition of this symbol when the
|
||||
* user has not specified one. Set this up by moving the runtime's copy of this
|
||||
* symbol to an object file within the archive.
|
||||
*/
|
||||
COMPILER_RT_WEAK char INSTR_PROF_PROFILE_NAME_VAR[1] = {0};
|
|
@ -0,0 +1,63 @@
|
|||
/*===- InstrProfilingPlatformDarwin.c - Profile data on Darwin ------------===*\
|
||||
|*
|
||||
|* The LLVM Compiler Infrastructure
|
||||
|*
|
||||
|* This file is distributed under the University of Illinois Open Source
|
||||
|* License. See LICENSE.TXT for details.
|
||||
|*
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
#include "InstrProfiling.h"
|
||||
|
||||
#if defined(__APPLE__)
|
||||
/* Use linker magic to find the bounds of the Data section. */
|
||||
COMPILER_RT_VISIBILITY
|
||||
extern __llvm_profile_data
|
||||
DataStart __asm("section$start$__DATA$" INSTR_PROF_DATA_SECT_NAME_STR);
|
||||
COMPILER_RT_VISIBILITY
|
||||
extern __llvm_profile_data
|
||||
DataEnd __asm("section$end$__DATA$" INSTR_PROF_DATA_SECT_NAME_STR);
|
||||
COMPILER_RT_VISIBILITY
|
||||
extern char
|
||||
NamesStart __asm("section$start$__DATA$" INSTR_PROF_NAME_SECT_NAME_STR);
|
||||
COMPILER_RT_VISIBILITY
|
||||
extern char NamesEnd __asm("section$end$__DATA$" INSTR_PROF_NAME_SECT_NAME_STR);
|
||||
COMPILER_RT_VISIBILITY
|
||||
extern uint64_t
|
||||
CountersStart __asm("section$start$__DATA$" INSTR_PROF_CNTS_SECT_NAME_STR);
|
||||
COMPILER_RT_VISIBILITY
|
||||
extern uint64_t
|
||||
CountersEnd __asm("section$end$__DATA$" INSTR_PROF_CNTS_SECT_NAME_STR);
|
||||
|
||||
COMPILER_RT_VISIBILITY
|
||||
extern ValueProfNode
|
||||
VNodesStart __asm("section$start$__DATA$" INSTR_PROF_VNODES_SECT_NAME_STR);
|
||||
COMPILER_RT_VISIBILITY
|
||||
extern ValueProfNode
|
||||
VNodesEnd __asm("section$end$__DATA$" INSTR_PROF_VNODES_SECT_NAME_STR);
|
||||
|
||||
COMPILER_RT_VISIBILITY
|
||||
const __llvm_profile_data *__llvm_profile_begin_data(void) {
|
||||
return &DataStart;
|
||||
}
|
||||
COMPILER_RT_VISIBILITY
|
||||
const __llvm_profile_data *__llvm_profile_end_data(void) { return &DataEnd; }
|
||||
COMPILER_RT_VISIBILITY
|
||||
const char *__llvm_profile_begin_names(void) { return &NamesStart; }
|
||||
COMPILER_RT_VISIBILITY
|
||||
const char *__llvm_profile_end_names(void) { return &NamesEnd; }
|
||||
COMPILER_RT_VISIBILITY
|
||||
uint64_t *__llvm_profile_begin_counters(void) { return &CountersStart; }
|
||||
COMPILER_RT_VISIBILITY
|
||||
uint64_t *__llvm_profile_end_counters(void) { return &CountersEnd; }
|
||||
|
||||
COMPILER_RT_VISIBILITY
|
||||
ValueProfNode *__llvm_profile_begin_vnodes(void) {
|
||||
return &VNodesStart;
|
||||
}
|
||||
COMPILER_RT_VISIBILITY
|
||||
ValueProfNode *__llvm_profile_end_vnodes(void) { return &VNodesEnd; }
|
||||
|
||||
COMPILER_RT_VISIBILITY ValueProfNode *CurrentVNode = &VNodesStart;
|
||||
COMPILER_RT_VISIBILITY ValueProfNode *EndVNode = &VNodesEnd;
|
||||
#endif
|
|
@ -0,0 +1,75 @@
|
|||
/*===- InstrProfilingPlatformLinux.c - Profile data Linux platform ------===*\
|
||||
|*
|
||||
|* The LLVM Compiler Infrastructure
|
||||
|*
|
||||
|* This file is distributed under the University of Illinois Open Source
|
||||
|* License. See LICENSE.TXT for details.
|
||||
|*
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
#include "InstrProfiling.h"
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
#include <stdlib.h>
|
||||
|
||||
#define PROF_DATA_START INSTR_PROF_SECT_START(INSTR_PROF_DATA_SECT_NAME)
|
||||
#define PROF_DATA_STOP INSTR_PROF_SECT_STOP(INSTR_PROF_DATA_SECT_NAME)
|
||||
#define PROF_NAME_START INSTR_PROF_SECT_START(INSTR_PROF_NAME_SECT_NAME)
|
||||
#define PROF_NAME_STOP INSTR_PROF_SECT_STOP(INSTR_PROF_NAME_SECT_NAME)
|
||||
#define PROF_CNTS_START INSTR_PROF_SECT_START(INSTR_PROF_CNTS_SECT_NAME)
|
||||
#define PROF_CNTS_STOP INSTR_PROF_SECT_STOP(INSTR_PROF_CNTS_SECT_NAME)
|
||||
#define PROF_VNODES_START INSTR_PROF_SECT_START(INSTR_PROF_VNODES_SECT_NAME)
|
||||
#define PROF_VNODES_STOP INSTR_PROF_SECT_STOP(INSTR_PROF_VNODES_SECT_NAME)
|
||||
|
||||
/* Declare section start and stop symbols for various sections
|
||||
* generated by compiler instrumentation.
|
||||
*/
|
||||
extern __llvm_profile_data PROF_DATA_START COMPILER_RT_VISIBILITY;
|
||||
extern __llvm_profile_data PROF_DATA_STOP COMPILER_RT_VISIBILITY;
|
||||
extern uint64_t PROF_CNTS_START COMPILER_RT_VISIBILITY;
|
||||
extern uint64_t PROF_CNTS_STOP COMPILER_RT_VISIBILITY;
|
||||
extern char PROF_NAME_START COMPILER_RT_VISIBILITY;
|
||||
extern char PROF_NAME_STOP COMPILER_RT_VISIBILITY;
|
||||
extern ValueProfNode PROF_VNODES_START COMPILER_RT_VISIBILITY;
|
||||
extern ValueProfNode PROF_VNODES_STOP COMPILER_RT_VISIBILITY;
|
||||
|
||||
/* Add dummy data to ensure the section is always created. */
|
||||
__llvm_profile_data
|
||||
__prof_data_sect_data[0] COMPILER_RT_SECTION(INSTR_PROF_DATA_SECT_NAME_STR);
|
||||
uint64_t
|
||||
__prof_cnts_sect_data[0] COMPILER_RT_SECTION(INSTR_PROF_CNTS_SECT_NAME_STR);
|
||||
char __prof_nms_sect_data[0] COMPILER_RT_SECTION(INSTR_PROF_NAME_SECT_NAME_STR);
|
||||
ValueProfNode __prof_vnodes_sect_data[0] COMPILER_RT_SECTION(INSTR_PROF_VNODES_SECT_NAME_STR);
|
||||
|
||||
COMPILER_RT_VISIBILITY const __llvm_profile_data *
|
||||
__llvm_profile_begin_data(void) {
|
||||
return &PROF_DATA_START;
|
||||
}
|
||||
COMPILER_RT_VISIBILITY const __llvm_profile_data *
|
||||
__llvm_profile_end_data(void) {
|
||||
return &PROF_DATA_STOP;
|
||||
}
|
||||
COMPILER_RT_VISIBILITY const char *__llvm_profile_begin_names(void) {
|
||||
return &PROF_NAME_START;
|
||||
}
|
||||
COMPILER_RT_VISIBILITY const char *__llvm_profile_end_names(void) {
|
||||
return &PROF_NAME_STOP;
|
||||
}
|
||||
COMPILER_RT_VISIBILITY uint64_t *__llvm_profile_begin_counters(void) {
|
||||
return &PROF_CNTS_START;
|
||||
}
|
||||
COMPILER_RT_VISIBILITY uint64_t *__llvm_profile_end_counters(void) {
|
||||
return &PROF_CNTS_STOP;
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY ValueProfNode *
|
||||
__llvm_profile_begin_vnodes(void) {
|
||||
return &PROF_VNODES_START;
|
||||
}
|
||||
COMPILER_RT_VISIBILITY ValueProfNode *__llvm_profile_end_vnodes(void) {
|
||||
return &PROF_VNODES_STOP;
|
||||
}
|
||||
COMPILER_RT_VISIBILITY ValueProfNode *CurrentVNode = &PROF_VNODES_START;
|
||||
COMPILER_RT_VISIBILITY ValueProfNode *EndVNode = &PROF_VNODES_STOP;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,94 @@
|
|||
/*===- InstrProfilingPlatformOther.c - Profile data default platform ------===*\
|
||||
|*
|
||||
|* The LLVM Compiler Infrastructure
|
||||
|*
|
||||
|* This file is distributed under the University of Illinois Open Source
|
||||
|* License. See LICENSE.TXT for details.
|
||||
|*
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
#include "InstrProfiling.h"
|
||||
|
||||
#if !defined(__APPLE__) && !defined(__linux__) && !defined(__FreeBSD__)
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
static const __llvm_profile_data *DataFirst = NULL;
|
||||
static const __llvm_profile_data *DataLast = NULL;
|
||||
static const char *NamesFirst = NULL;
|
||||
static const char *NamesLast = NULL;
|
||||
static uint64_t *CountersFirst = NULL;
|
||||
static uint64_t *CountersLast = NULL;
|
||||
|
||||
static const void *getMinAddr(const void *A1, const void *A2) {
|
||||
return A1 < A2 ? A1 : A2;
|
||||
}
|
||||
|
||||
static const void *getMaxAddr(const void *A1, const void *A2) {
|
||||
return A1 > A2 ? A1 : A2;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Register an instrumented function.
|
||||
*
|
||||
* Calls to this are emitted by clang with -fprofile-instr-generate. Such
|
||||
* calls are only required (and only emitted) on targets where we haven't
|
||||
* implemented linker magic to find the bounds of the sections.
|
||||
*/
|
||||
COMPILER_RT_VISIBILITY
|
||||
void __llvm_profile_register_function(void *Data_) {
|
||||
/* TODO: Only emit this function if we can't use linker magic. */
|
||||
const __llvm_profile_data *Data = (__llvm_profile_data *)Data_;
|
||||
if (!DataFirst) {
|
||||
DataFirst = Data;
|
||||
DataLast = Data + 1;
|
||||
CountersFirst = Data->CounterPtr;
|
||||
CountersLast = (uint64_t *)Data->CounterPtr + Data->NumCounters;
|
||||
return;
|
||||
}
|
||||
|
||||
DataFirst = (const __llvm_profile_data *)getMinAddr(DataFirst, Data);
|
||||
CountersFirst = (uint64_t *)getMinAddr(CountersFirst, Data->CounterPtr);
|
||||
|
||||
DataLast = (const __llvm_profile_data *)getMaxAddr(DataLast, Data + 1);
|
||||
CountersLast = (uint64_t *)getMaxAddr(
|
||||
CountersLast, (uint64_t *)Data->CounterPtr + Data->NumCounters);
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY
|
||||
void __llvm_profile_register_names_function(void *NamesStart,
|
||||
uint64_t NamesSize) {
|
||||
if (!NamesFirst) {
|
||||
NamesFirst = (const char *)NamesStart;
|
||||
NamesLast = (const char *)NamesStart + NamesSize;
|
||||
return;
|
||||
}
|
||||
NamesFirst = (const char *)getMinAddr(NamesFirst, NamesStart);
|
||||
NamesLast =
|
||||
(const char *)getMaxAddr(NamesLast, (const char *)NamesStart + NamesSize);
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY
|
||||
const __llvm_profile_data *__llvm_profile_begin_data(void) { return DataFirst; }
|
||||
COMPILER_RT_VISIBILITY
|
||||
const __llvm_profile_data *__llvm_profile_end_data(void) { return DataLast; }
|
||||
COMPILER_RT_VISIBILITY
|
||||
const char *__llvm_profile_begin_names(void) { return NamesFirst; }
|
||||
COMPILER_RT_VISIBILITY
|
||||
const char *__llvm_profile_end_names(void) { return NamesLast; }
|
||||
COMPILER_RT_VISIBILITY
|
||||
uint64_t *__llvm_profile_begin_counters(void) { return CountersFirst; }
|
||||
COMPILER_RT_VISIBILITY
|
||||
uint64_t *__llvm_profile_end_counters(void) { return CountersLast; }
|
||||
|
||||
COMPILER_RT_VISIBILITY
|
||||
ValueProfNode *__llvm_profile_begin_vnodes(void) {
|
||||
return 0;
|
||||
}
|
||||
COMPILER_RT_VISIBILITY
|
||||
ValueProfNode *__llvm_profile_end_vnodes(void) { return 0; }
|
||||
|
||||
COMPILER_RT_VISIBILITY ValueProfNode *CurrentVNode = 0;
|
||||
COMPILER_RT_VISIBILITY ValueProfNode *EndVNode = 0;
|
||||
|
||||
#endif
|
122
runtime/profile-rt/profile-rt-70/InstrProfilingPort.h
Normal file
122
runtime/profile-rt/profile-rt-70/InstrProfilingPort.h
Normal file
|
@ -0,0 +1,122 @@
|
|||
/*===- InstrProfilingPort.h- Support library for PGO instrumentation ------===*\
|
||||
|*
|
||||
|* The LLVM Compiler Infrastructure
|
||||
|*
|
||||
|* This file is distributed under the University of Illinois Open Source
|
||||
|* License. See LICENSE.TXT for details.
|
||||
|*
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
#ifndef PROFILE_INSTRPROFILING_PORT_H_
|
||||
#define PROFILE_INSTRPROFILING_PORT_H_
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define COMPILER_RT_ALIGNAS(x) __declspec(align(x))
|
||||
#define COMPILER_RT_VISIBILITY
|
||||
/* FIXME: selectany does not have the same semantics as weak. */
|
||||
#define COMPILER_RT_WEAK __declspec(selectany)
|
||||
/* Need to include <windows.h> */
|
||||
#define COMPILER_RT_ALLOCA _alloca
|
||||
/* Need to include <stdio.h> and <io.h> */
|
||||
#define COMPILER_RT_FTRUNCATE(f,l) _chsize(_fileno(f),l)
|
||||
#elif __GNUC__
|
||||
#define COMPILER_RT_ALIGNAS(x) __attribute__((aligned(x)))
|
||||
#define COMPILER_RT_VISIBILITY __attribute__((visibility("hidden")))
|
||||
#define COMPILER_RT_WEAK __attribute__((weak))
|
||||
#define COMPILER_RT_ALLOCA __builtin_alloca
|
||||
#define COMPILER_RT_FTRUNCATE(f,l) ftruncate(fileno(f),l)
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#define COMPILER_RT_SEG "__DATA,"
|
||||
#else
|
||||
#define COMPILER_RT_SEG ""
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define COMPILER_RT_SECTION(Sect) __declspec(allocate(Sect))
|
||||
#else
|
||||
#define COMPILER_RT_SECTION(Sect) __attribute__((section(Sect)))
|
||||
#endif
|
||||
|
||||
#define COMPILER_RT_MAX_HOSTLEN 128
|
||||
#ifdef __ORBIS__
|
||||
#define COMPILER_RT_GETHOSTNAME(Name, Len) ((void)(Name), (void)(Len), (-1))
|
||||
#else
|
||||
#define COMPILER_RT_GETHOSTNAME(Name, Len) lprofGetHostName(Name, Len)
|
||||
#ifndef _MSC_VER
|
||||
#define COMPILER_RT_HAS_UNAME 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if COMPILER_RT_HAS_ATOMICS == 1
|
||||
#ifdef _MSC_VER
|
||||
#include <windows.h>
|
||||
#if _MSC_VER < 1900
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
#if defined(_WIN64)
|
||||
#define COMPILER_RT_BOOL_CMPXCHG(Ptr, OldV, NewV) \
|
||||
(InterlockedCompareExchange64((LONGLONG volatile *)Ptr, (LONGLONG)NewV, \
|
||||
(LONGLONG)OldV) == (LONGLONG)OldV)
|
||||
#define COMPILER_RT_PTR_FETCH_ADD(DomType, PtrVar, PtrIncr) \
|
||||
(DomType *)InterlockedExchangeAdd64((LONGLONG volatile *)&PtrVar, \
|
||||
(LONGLONG)sizeof(DomType) * PtrIncr)
|
||||
#else /* !defined(_WIN64) */
|
||||
#define COMPILER_RT_BOOL_CMPXCHG(Ptr, OldV, NewV) \
|
||||
(InterlockedCompareExchange((LONG volatile *)Ptr, (LONG)NewV, (LONG)OldV) == \
|
||||
(LONG)OldV)
|
||||
#define COMPILER_RT_PTR_FETCH_ADD(DomType, PtrVar, PtrIncr) \
|
||||
(DomType *)InterlockedExchangeAdd((LONG volatile *)&PtrVar, \
|
||||
(LONG)sizeof(DomType) * PtrIncr)
|
||||
#endif
|
||||
#else /* !defined(_MSC_VER) */
|
||||
#define COMPILER_RT_BOOL_CMPXCHG(Ptr, OldV, NewV) \
|
||||
__sync_bool_compare_and_swap(Ptr, OldV, NewV)
|
||||
#define COMPILER_RT_PTR_FETCH_ADD(DomType, PtrVar, PtrIncr) \
|
||||
(DomType *)__sync_fetch_and_add((long *)&PtrVar, sizeof(DomType) * PtrIncr)
|
||||
#endif
|
||||
#else /* COMPILER_RT_HAS_ATOMICS != 1 */
|
||||
#include "InstrProfilingUtil.h"
|
||||
#define COMPILER_RT_BOOL_CMPXCHG(Ptr, OldV, NewV) \
|
||||
lprofBoolCmpXchg((void **)Ptr, OldV, NewV)
|
||||
#define COMPILER_RT_PTR_FETCH_ADD(DomType, PtrVar, PtrIncr) \
|
||||
(DomType *)lprofPtrFetchAdd((void **)&PtrVar, sizeof(DomType) * PtrIncr)
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define DIR_SEPARATOR '\\'
|
||||
#define DIR_SEPARATOR_2 '/'
|
||||
#else
|
||||
#define DIR_SEPARATOR '/'
|
||||
#endif
|
||||
|
||||
#ifndef DIR_SEPARATOR_2
|
||||
#define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR)
|
||||
#else /* DIR_SEPARATOR_2 */
|
||||
#define IS_DIR_SEPARATOR(ch) \
|
||||
(((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2))
|
||||
#endif /* DIR_SEPARATOR_2 */
|
||||
|
||||
#define PROF_ERR(Format, ...) \
|
||||
fprintf(stderr, "LLVM Profile Error: " Format, __VA_ARGS__);
|
||||
|
||||
#define PROF_WARN(Format, ...) \
|
||||
fprintf(stderr, "LLVM Profile Warning: " Format, __VA_ARGS__);
|
||||
|
||||
#define PROF_NOTE(Format, ...) \
|
||||
fprintf(stderr, "LLVM Profile Note: " Format, __VA_ARGS__);
|
||||
|
||||
#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_ */
|
30
runtime/profile-rt/profile-rt-70/InstrProfilingRuntime.cc
Normal file
30
runtime/profile-rt/profile-rt-70/InstrProfilingRuntime.cc
Normal file
|
@ -0,0 +1,30 @@
|
|||
//===- InstrProfilingRuntime.cpp - PGO runtime initialization -------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include "InstrProfiling.h"
|
||||
|
||||
/* int __llvm_profile_runtime */
|
||||
COMPILER_RT_VISIBILITY int INSTR_PROF_PROFILE_RUNTIME_VAR;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class RegisterRuntime {
|
||||
public:
|
||||
RegisterRuntime() {
|
||||
__llvm_profile_register_write_file_atexit();
|
||||
__llvm_profile_initialize_file();
|
||||
}
|
||||
};
|
||||
|
||||
RegisterRuntime Registration;
|
||||
|
||||
}
|
244
runtime/profile-rt/profile-rt-70/InstrProfilingUtil.c
Normal file
244
runtime/profile-rt/profile-rt-70/InstrProfilingUtil.c
Normal file
|
@ -0,0 +1,244 @@
|
|||
/*===- InstrProfilingUtil.c - Support library for PGO instrumentation -----===*\
|
||||
|*
|
||||
|* The LLVM Compiler Infrastructure
|
||||
|*
|
||||
|* This file is distributed under the University of Illinois Open Source
|
||||
|* License. See LICENSE.TXT for details.
|
||||
|*
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
#include "InstrProfilingUtil.h"
|
||||
#include "InstrProfiling.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
#include <io.h>
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#ifdef COMPILER_RT_HAS_UNAME
|
||||
#include <sys/utsname.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <signal.h>
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
|
||||
COMPILER_RT_VISIBILITY
|
||||
void __llvm_profile_recursive_mkdir(char *path) {
|
||||
int i;
|
||||
|
||||
for (i = 1; path[i] != '\0'; ++i) {
|
||||
char save = path[i];
|
||||
if (!IS_DIR_SEPARATOR(path[i]))
|
||||
continue;
|
||||
path[i] = '\0';
|
||||
#ifdef _WIN32
|
||||
_mkdir(path);
|
||||
#else
|
||||
mkdir(path, 0755); /* Some of these will fail, ignore it. */
|
||||
#endif
|
||||
path[i] = save;
|
||||
}
|
||||
}
|
||||
|
||||
#if COMPILER_RT_HAS_ATOMICS != 1
|
||||
COMPILER_RT_VISIBILITY
|
||||
uint32_t lprofBoolCmpXchg(void **Ptr, void *OldV, void *NewV) {
|
||||
void *R = *Ptr;
|
||||
if (R == OldV) {
|
||||
*Ptr = NewV;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
COMPILER_RT_VISIBILITY
|
||||
void *lprofPtrFetchAdd(void **Mem, long ByteIncr) {
|
||||
void *Old = *Mem;
|
||||
*((char **)Mem) += ByteIncr;
|
||||
return Old;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
COMPILER_RT_VISIBILITY int lprofGetHostName(char *Name, int Len) {
|
||||
WCHAR Buffer[COMPILER_RT_MAX_HOSTLEN];
|
||||
DWORD BufferSize = sizeof(Buffer);
|
||||
BOOL Result =
|
||||
GetComputerNameExW(ComputerNameDnsFullyQualified, Buffer, &BufferSize);
|
||||
if (!Result)
|
||||
return -1;
|
||||
if (WideCharToMultiByte(CP_UTF8, 0, Buffer, -1, Name, Len, NULL, NULL) == 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
#elif defined(COMPILER_RT_HAS_UNAME)
|
||||
COMPILER_RT_VISIBILITY int lprofGetHostName(char *Name, int Len) {
|
||||
struct utsname N;
|
||||
int R;
|
||||
if (!(R = uname(&N)))
|
||||
strncpy(Name, N.nodename, Len);
|
||||
return R;
|
||||
}
|
||||
#endif
|
||||
|
||||
COMPILER_RT_VISIBILITY FILE *lprofOpenFileEx(const char *ProfileName) {
|
||||
FILE *f;
|
||||
int fd;
|
||||
#ifdef COMPILER_RT_HAS_FCNTL_LCK
|
||||
struct flock s_flock;
|
||||
|
||||
s_flock.l_whence = SEEK_SET;
|
||||
s_flock.l_start = 0;
|
||||
s_flock.l_len = 0; /* Until EOF. */
|
||||
s_flock.l_pid = getpid();
|
||||
|
||||
s_flock.l_type = F_WRLCK;
|
||||
fd = open(ProfileName, O_RDWR | O_CREAT, 0666);
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
|
||||
while (fcntl(fd, F_SETLKW, &s_flock) == -1) {
|
||||
if (errno != EINTR) {
|
||||
if (errno == ENOLCK) {
|
||||
PROF_WARN("Data may be corrupted during profile merging : %s\n",
|
||||
"Fail to obtain file lock due to system limit.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
f = fdopen(fd, "r+b");
|
||||
#elif defined(_WIN32)
|
||||
// FIXME: Use the wide variants to handle Unicode filenames.
|
||||
HANDLE h = CreateFileA(ProfileName, GENERIC_READ | GENERIC_WRITE, 0, 0,
|
||||
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
if (h == INVALID_HANDLE_VALUE)
|
||||
return NULL;
|
||||
|
||||
fd = _open_osfhandle((intptr_t)h, 0);
|
||||
if (fd == -1) {
|
||||
CloseHandle(h);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
f = _fdopen(fd, "r+b");
|
||||
if (f == 0) {
|
||||
CloseHandle(h);
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
/* Worst case no locking applied. */
|
||||
PROF_WARN("Concurrent file access is not supported : %s\n",
|
||||
"lack file locking");
|
||||
fd = open(ProfileName, O_RDWR | O_CREAT, 0666);
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
f = fdopen(fd, "r+b");
|
||||
#endif
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY const char *lprofGetPathPrefix(int *PrefixStrip,
|
||||
size_t *PrefixLen) {
|
||||
const char *Prefix = getenv("GCOV_PREFIX");
|
||||
const char *PrefixStripStr = getenv("GCOV_PREFIX_STRIP");
|
||||
|
||||
*PrefixLen = 0;
|
||||
*PrefixStrip = 0;
|
||||
if (Prefix == NULL || Prefix[0] == '\0')
|
||||
return NULL;
|
||||
|
||||
if (PrefixStripStr) {
|
||||
*PrefixStrip = atoi(PrefixStripStr);
|
||||
|
||||
/* Negative GCOV_PREFIX_STRIP values are ignored */
|
||||
if (*PrefixStrip < 0)
|
||||
*PrefixStrip = 0;
|
||||
} else {
|
||||
*PrefixStrip = 0;
|
||||
}
|
||||
*PrefixLen = strlen(Prefix);
|
||||
|
||||
return Prefix;
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY void
|
||||
lprofApplyPathPrefix(char *Dest, const char *PathStr, const char *Prefix,
|
||||
size_t PrefixLen, int PrefixStrip) {
|
||||
|
||||
const char *Ptr;
|
||||
int Level;
|
||||
const char *StrippedPathStr = PathStr;
|
||||
|
||||
for (Level = 0, Ptr = PathStr + 1; Level < PrefixStrip; ++Ptr) {
|
||||
if (*Ptr == '\0')
|
||||
break;
|
||||
|
||||
if (!IS_DIR_SEPARATOR(*Ptr))
|
||||
continue;
|
||||
|
||||
StrippedPathStr = Ptr;
|
||||
++Level;
|
||||
}
|
||||
|
||||
memcpy(Dest, Prefix, PrefixLen);
|
||||
|
||||
if (!IS_DIR_SEPARATOR(Prefix[PrefixLen - 1]))
|
||||
Dest[PrefixLen++] = DIR_SEPARATOR;
|
||||
|
||||
memcpy(Dest + PrefixLen, StrippedPathStr, strlen(StrippedPathStr) + 1);
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY const char *
|
||||
lprofFindFirstDirSeparator(const char *Path) {
|
||||
const char *Sep;
|
||||
Sep = strchr(Path, DIR_SEPARATOR);
|
||||
if (Sep)
|
||||
return Sep;
|
||||
#if defined(DIR_SEPARATOR_2)
|
||||
Sep = strchr(Path, DIR_SEPARATOR_2);
|
||||
#endif
|
||||
return Sep;
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY const char *lprofFindLastDirSeparator(const char *Path) {
|
||||
const char *Sep;
|
||||
Sep = strrchr(Path, DIR_SEPARATOR);
|
||||
if (Sep)
|
||||
return Sep;
|
||||
#if defined(DIR_SEPARATOR_2)
|
||||
Sep = strrchr(Path, DIR_SEPARATOR_2);
|
||||
#endif
|
||||
return Sep;
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY int lprofSuspendSigKill() {
|
||||
#if defined(__linux__)
|
||||
int PDeachSig = 0;
|
||||
/* Temporarily suspend getting SIGKILL upon exit of the parent process. */
|
||||
if (prctl(PR_GET_PDEATHSIG, &PDeachSig) == 0 && PDeachSig == SIGKILL)
|
||||
prctl(PR_SET_PDEATHSIG, 0);
|
||||
return (PDeachSig == SIGKILL);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY void lprofRestoreSigKill() {
|
||||
#if defined(__linux__)
|
||||
prctl(PR_SET_PDEATHSIG, SIGKILL);
|
||||
#endif
|
||||
}
|
62
runtime/profile-rt/profile-rt-70/InstrProfilingUtil.h
Normal file
62
runtime/profile-rt/profile-rt-70/InstrProfilingUtil.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*===- InstrProfilingUtil.h - Support library for PGO instrumentation -----===*\
|
||||
|*
|
||||
|* The LLVM Compiler Infrastructure
|
||||
|*
|
||||
|* This file is distributed under the University of Illinois Open Source
|
||||
|* License. See LICENSE.TXT for details.
|
||||
|*
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
#ifndef PROFILE_INSTRPROFILINGUTIL_H
|
||||
#define PROFILE_INSTRPROFILINGUTIL_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/*! \brief Create a directory tree. */
|
||||
void __llvm_profile_recursive_mkdir(char *Pathname);
|
||||
|
||||
/*! Open file \c Filename for read+write with write
|
||||
* lock for exclusive access. The caller will block
|
||||
* if the lock is already held by another process. */
|
||||
FILE *lprofOpenFileEx(const char *Filename);
|
||||
/* PS4 doesn't have getenv. Define a shim. */
|
||||
#if __ORBIS__
|
||||
static inline char *getenv(const char *name) { return NULL; }
|
||||
#endif /* #if __ORBIS__ */
|
||||
|
||||
/* GCOV_PREFIX and GCOV_PREFIX_STRIP support */
|
||||
/* Return the path prefix specified by GCOV_PREFIX environment variable.
|
||||
* If GCOV_PREFIX_STRIP is also specified, the strip level (integer value)
|
||||
* is returned via \c *PrefixStrip. The prefix length is stored in *PrefixLen.
|
||||
*/
|
||||
const char *lprofGetPathPrefix(int *PrefixStrip, size_t *PrefixLen);
|
||||
/* Apply the path prefix specified in \c Prefix to path string in \c PathStr,
|
||||
* and store the result to buffer pointed to by \c Buffer. If \c PrefixStrip
|
||||
* is not zero, path prefixes are stripped from \c PathStr (the level of
|
||||
* stripping is specified by \c PrefixStrip) before \c Prefix is added.
|
||||
*/
|
||||
void lprofApplyPathPrefix(char *Dest, const char *PathStr, const char *Prefix,
|
||||
size_t PrefixLen, int PrefixStrip);
|
||||
|
||||
/* Returns a pointer to the first occurrence of \c DIR_SEPARATOR char in
|
||||
* the string \c Path, or NULL if the char is not found. */
|
||||
const char *lprofFindFirstDirSeparator(const char *Path);
|
||||
/* Returns a pointer to the last occurrence of \c DIR_SEPARATOR char in
|
||||
* the string \c Path, or NULL if the char is not found. */
|
||||
const char *lprofFindLastDirSeparator(const char *Path);
|
||||
|
||||
int lprofGetHostName(char *Name, int Len);
|
||||
|
||||
unsigned lprofBoolCmpXchg(void **Ptr, void *OldV, void *NewV);
|
||||
void *lprofPtrFetchAdd(void **Mem, long ByteIncr);
|
||||
|
||||
/* Temporarily suspend SIGKILL. Return value of 1 means a restore is needed.
|
||||
* Other return values mean no restore is needed.
|
||||
*/
|
||||
int lprofSuspendSigKill();
|
||||
|
||||
/* Restore previously suspended SIGKILL. */
|
||||
void lprofRestoreSigKill();
|
||||
|
||||
#endif /* PROFILE_INSTRPROFILINGUTIL_H */
|
356
runtime/profile-rt/profile-rt-70/InstrProfilingValue.c
Normal file
356
runtime/profile-rt/profile-rt-70/InstrProfilingValue.c
Normal file
|
@ -0,0 +1,356 @@
|
|||
/*===- InstrProfilingValue.c - Support library for PGO instrumentation ----===*\
|
||||
|*
|
||||
|* The LLVM Compiler Infrastructure
|
||||
|*
|
||||
|* This file is distributed under the University of Illinois Open Source
|
||||
|* License. See LICENSE.TXT for details.
|
||||
|*
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
#include "InstrProfiling.h"
|
||||
#include "InstrProfilingInternal.h"
|
||||
#include "InstrProfilingUtil.h" /* For PS4 getenv shim. */
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#define INSTR_PROF_VALUE_PROF_DATA
|
||||
#define INSTR_PROF_COMMON_API_IMPL
|
||||
#include "InstrProfData.inc"
|
||||
|
||||
static int hasStaticCounters = 1;
|
||||
static int OutOfNodesWarnings = 0;
|
||||
static int hasNonDefaultValsPerSite = 0;
|
||||
#define INSTR_PROF_MAX_VP_WARNS 10
|
||||
#define INSTR_PROF_DEFAULT_NUM_VAL_PER_SITE 16
|
||||
#define INSTR_PROF_VNODE_POOL_SIZE 1024
|
||||
|
||||
#ifndef _MSC_VER
|
||||
/* A shared static pool in addition to the vnodes statically
|
||||
* allocated by the compiler. */
|
||||
COMPILER_RT_VISIBILITY ValueProfNode
|
||||
lprofValueProfNodes[INSTR_PROF_VNODE_POOL_SIZE] COMPILER_RT_SECTION(
|
||||
COMPILER_RT_SEG INSTR_PROF_VNODES_SECT_NAME_STR);
|
||||
#endif
|
||||
|
||||
COMPILER_RT_VISIBILITY uint32_t VPMaxNumValsPerSite =
|
||||
INSTR_PROF_DEFAULT_NUM_VAL_PER_SITE;
|
||||
|
||||
COMPILER_RT_VISIBILITY void lprofSetupValueProfiler() {
|
||||
const char *Str = 0;
|
||||
Str = getenv("LLVM_VP_MAX_NUM_VALS_PER_SITE");
|
||||
if (Str && Str[0]) {
|
||||
VPMaxNumValsPerSite = atoi(Str);
|
||||
hasNonDefaultValsPerSite = 1;
|
||||
}
|
||||
if (VPMaxNumValsPerSite > INSTR_PROF_MAX_NUM_VAL_PER_SITE)
|
||||
VPMaxNumValsPerSite = INSTR_PROF_MAX_NUM_VAL_PER_SITE;
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY void lprofSetMaxValsPerSite(uint32_t MaxVals) {
|
||||
VPMaxNumValsPerSite = MaxVals;
|
||||
hasNonDefaultValsPerSite = 1;
|
||||
}
|
||||
|
||||
/* This method is only used in value profiler mock testing. */
|
||||
COMPILER_RT_VISIBILITY void
|
||||
__llvm_profile_set_num_value_sites(__llvm_profile_data *Data,
|
||||
uint32_t ValueKind, uint16_t NumValueSites) {
|
||||
*((uint16_t *)&Data->NumValueSites[ValueKind]) = NumValueSites;
|
||||
}
|
||||
|
||||
/* This method is only used in value profiler mock testing. */
|
||||
COMPILER_RT_VISIBILITY const __llvm_profile_data *
|
||||
__llvm_profile_iterate_data(const __llvm_profile_data *Data) {
|
||||
return Data + 1;
|
||||
}
|
||||
|
||||
/* This method is only used in value profiler mock testing. */
|
||||
COMPILER_RT_VISIBILITY void *
|
||||
__llvm_get_function_addr(const __llvm_profile_data *Data) {
|
||||
return Data->FunctionPointer;
|
||||
}
|
||||
|
||||
/* Allocate an array that holds the pointers to the linked lists of
|
||||
* value profile counter nodes. The number of element of the array
|
||||
* is the total number of value profile sites instrumented. Returns
|
||||
* 0 if allocation fails.
|
||||
*/
|
||||
|
||||
static int allocateValueProfileCounters(__llvm_profile_data *Data) {
|
||||
uint64_t NumVSites = 0;
|
||||
uint32_t VKI;
|
||||
|
||||
/* This function will never be called when value site array is allocated
|
||||
statically at compile time. */
|
||||
hasStaticCounters = 0;
|
||||
/* When dynamic allocation is enabled, allow tracking the max number of
|
||||
* values allowd. */
|
||||
if (!hasNonDefaultValsPerSite)
|
||||
VPMaxNumValsPerSite = INSTR_PROF_MAX_NUM_VAL_PER_SITE;
|
||||
|
||||
for (VKI = IPVK_First; VKI <= IPVK_Last; ++VKI)
|
||||
NumVSites += Data->NumValueSites[VKI];
|
||||
|
||||
ValueProfNode **Mem =
|
||||
(ValueProfNode **)calloc(NumVSites, sizeof(ValueProfNode *));
|
||||
if (!Mem)
|
||||
return 0;
|
||||
if (!COMPILER_RT_BOOL_CMPXCHG(&Data->Values, 0, Mem)) {
|
||||
free(Mem);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static ValueProfNode *allocateOneNode(__llvm_profile_data *Data, uint32_t Index,
|
||||
uint64_t Value) {
|
||||
ValueProfNode *Node;
|
||||
|
||||
if (!hasStaticCounters)
|
||||
return (ValueProfNode *)calloc(1, sizeof(ValueProfNode));
|
||||
|
||||
/* Early check to avoid value wrapping around. */
|
||||
if (CurrentVNode + 1 > EndVNode) {
|
||||
if (OutOfNodesWarnings++ < INSTR_PROF_MAX_VP_WARNS) {
|
||||
PROF_WARN("Unable to track new values: %s. "
|
||||
" Consider using option -mllvm -vp-counters-per-site=<n> to "
|
||||
"allocate more"
|
||||
" value profile counters at compile time. \n",
|
||||
"Running out of static counters");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Node = COMPILER_RT_PTR_FETCH_ADD(ValueProfNode, CurrentVNode, 1);
|
||||
/* Due to section padding, EndVNode point to a byte which is one pass
|
||||
* an incomplete VNode, so we need to skip the last incomplete node. */
|
||||
if (Node + 1 > EndVNode)
|
||||
return 0;
|
||||
|
||||
return Node;
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY void
|
||||
__llvm_profile_instrument_target(uint64_t TargetValue, void *Data,
|
||||
uint32_t CounterIndex) {
|
||||
__llvm_profile_data *PData = (__llvm_profile_data *)Data;
|
||||
if (!PData)
|
||||
return;
|
||||
|
||||
if (!PData->Values) {
|
||||
if (!allocateValueProfileCounters(PData))
|
||||
return;
|
||||
}
|
||||
|
||||
ValueProfNode **ValueCounters = (ValueProfNode **)PData->Values;
|
||||
ValueProfNode *PrevVNode = NULL;
|
||||
ValueProfNode *MinCountVNode = NULL;
|
||||
ValueProfNode *CurVNode = ValueCounters[CounterIndex];
|
||||
uint64_t MinCount = UINT64_MAX;
|
||||
|
||||
uint8_t VDataCount = 0;
|
||||
while (CurVNode) {
|
||||
if (TargetValue == CurVNode->Value) {
|
||||
CurVNode->Count++;
|
||||
return;
|
||||
}
|
||||
if (CurVNode->Count < MinCount) {
|
||||
MinCount = CurVNode->Count;
|
||||
MinCountVNode = CurVNode;
|
||||
}
|
||||
PrevVNode = CurVNode;
|
||||
CurVNode = CurVNode->Next;
|
||||
++VDataCount;
|
||||
}
|
||||
|
||||
if (VDataCount >= VPMaxNumValsPerSite) {
|
||||
/* Bump down the min count node's count. If it reaches 0,
|
||||
* evict it. This eviction/replacement policy makes hot
|
||||
* targets more sticky while cold targets less so. In other
|
||||
* words, it makes it less likely for the hot targets to be
|
||||
* prematurally evicted during warmup/establishment period,
|
||||
* when their counts are still low. In a special case when
|
||||
* the number of values tracked is reduced to only one, this
|
||||
* policy will guarantee that the dominating target with >50%
|
||||
* total count will survive in the end. Note that this scheme
|
||||
* allows the runtime to track the min count node in an adaptive
|
||||
* manner. It can correct previous mistakes and eventually
|
||||
* lock on a cold target that is alread in stable state.
|
||||
*
|
||||
* In very rare cases, this replacement scheme may still lead
|
||||
* to target loss. For instance, out of \c N value slots, \c N-1
|
||||
* slots are occupied by luke warm targets during the warmup
|
||||
* period and the remaining one slot is competed by two or more
|
||||
* very hot targets. If those hot targets occur in an interleaved
|
||||
* way, none of them will survive (gain enough weight to throw out
|
||||
* other established entries) due to the ping-pong effect.
|
||||
* To handle this situation, user can choose to increase the max
|
||||
* number of tracked values per value site. Alternatively, a more
|
||||
* expensive eviction mechanism can be implemented. It requires
|
||||
* the runtime to track the total number of evictions per-site.
|
||||
* When the total number of evictions reaches certain threshold,
|
||||
* the runtime can wipe out more than one lowest count entries
|
||||
* to give space for hot targets.
|
||||
*/
|
||||
if (!MinCountVNode->Count || !(--MinCountVNode->Count)) {
|
||||
CurVNode = MinCountVNode;
|
||||
CurVNode->Value = TargetValue;
|
||||
CurVNode->Count++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
CurVNode = allocateOneNode(PData, CounterIndex, TargetValue);
|
||||
if (!CurVNode)
|
||||
return;
|
||||
CurVNode->Value = TargetValue;
|
||||
CurVNode->Count++;
|
||||
|
||||
uint32_t Success = 0;
|
||||
if (!ValueCounters[CounterIndex])
|
||||
Success =
|
||||
COMPILER_RT_BOOL_CMPXCHG(&ValueCounters[CounterIndex], 0, CurVNode);
|
||||
else if (PrevVNode && !PrevVNode->Next)
|
||||
Success = COMPILER_RT_BOOL_CMPXCHG(&(PrevVNode->Next), 0, CurVNode);
|
||||
|
||||
if (!Success && !hasStaticCounters) {
|
||||
free(CurVNode);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The target values are partitioned into multiple regions/ranges. There is one
|
||||
* contiguous region which is precise -- every value in the range is tracked
|
||||
* individually. A value outside the precise region will be collapsed into one
|
||||
* value depending on the region it falls in.
|
||||
*
|
||||
* There are three regions:
|
||||
* 1. (-inf, PreciseRangeStart) and (PreciseRangeLast, LargeRangeValue) belong
|
||||
* to one region -- all values here should be mapped to one value of
|
||||
* "PreciseRangeLast + 1".
|
||||
* 2. [PreciseRangeStart, PreciseRangeLast]
|
||||
* 3. Large values: [LargeValue, +inf) maps to one value of LargeValue.
|
||||
*
|
||||
* The range for large values is optional. The default value of INT64_MIN
|
||||
* indicates it is not specified.
|
||||
*/
|
||||
COMPILER_RT_VISIBILITY void __llvm_profile_instrument_range(
|
||||
uint64_t TargetValue, void *Data, uint32_t CounterIndex,
|
||||
int64_t PreciseRangeStart, int64_t PreciseRangeLast, int64_t LargeValue) {
|
||||
|
||||
if (LargeValue != INT64_MIN && (int64_t)TargetValue >= LargeValue)
|
||||
TargetValue = LargeValue;
|
||||
else if ((int64_t)TargetValue < PreciseRangeStart ||
|
||||
(int64_t)TargetValue > PreciseRangeLast)
|
||||
TargetValue = PreciseRangeLast + 1;
|
||||
|
||||
__llvm_profile_instrument_target(TargetValue, Data, CounterIndex);
|
||||
}
|
||||
|
||||
/*
|
||||
* A wrapper struct that represents value profile runtime data.
|
||||
* Like InstrProfRecord class which is used by profiling host tools,
|
||||
* ValueProfRuntimeRecord also implements the abstract intefaces defined in
|
||||
* ValueProfRecordClosure so that the runtime data can be serialized using
|
||||
* shared C implementation.
|
||||
*/
|
||||
typedef struct ValueProfRuntimeRecord {
|
||||
const __llvm_profile_data *Data;
|
||||
ValueProfNode **NodesKind[IPVK_Last + 1];
|
||||
uint8_t **SiteCountArray;
|
||||
} ValueProfRuntimeRecord;
|
||||
|
||||
/* ValueProfRecordClosure Interface implementation. */
|
||||
|
||||
static uint32_t getNumValueSitesRT(const void *R, uint32_t VK) {
|
||||
return ((const ValueProfRuntimeRecord *)R)->Data->NumValueSites[VK];
|
||||
}
|
||||
|
||||
static uint32_t getNumValueDataRT(const void *R, uint32_t VK) {
|
||||
uint32_t S = 0, I;
|
||||
const ValueProfRuntimeRecord *Record = (const ValueProfRuntimeRecord *)R;
|
||||
if (Record->SiteCountArray[VK] == INSTR_PROF_NULLPTR)
|
||||
return 0;
|
||||
for (I = 0; I < Record->Data->NumValueSites[VK]; I++)
|
||||
S += Record->SiteCountArray[VK][I];
|
||||
return S;
|
||||
}
|
||||
|
||||
static uint32_t getNumValueDataForSiteRT(const void *R, uint32_t VK,
|
||||
uint32_t S) {
|
||||
const ValueProfRuntimeRecord *Record = (const ValueProfRuntimeRecord *)R;
|
||||
return Record->SiteCountArray[VK][S];
|
||||
}
|
||||
|
||||
static ValueProfRuntimeRecord RTRecord;
|
||||
static ValueProfRecordClosure RTRecordClosure = {
|
||||
&RTRecord, INSTR_PROF_NULLPTR, /* GetNumValueKinds */
|
||||
getNumValueSitesRT, getNumValueDataRT, getNumValueDataForSiteRT,
|
||||
INSTR_PROF_NULLPTR, /* RemapValueData */
|
||||
INSTR_PROF_NULLPTR, /* GetValueForSite, */
|
||||
INSTR_PROF_NULLPTR /* AllocValueProfData */
|
||||
};
|
||||
|
||||
static uint32_t
|
||||
initializeValueProfRuntimeRecord(const __llvm_profile_data *Data,
|
||||
uint8_t *SiteCountArray[]) {
|
||||
unsigned I, J, S = 0, NumValueKinds = 0;
|
||||
ValueProfNode **Nodes = (ValueProfNode **)Data->Values;
|
||||
RTRecord.Data = Data;
|
||||
RTRecord.SiteCountArray = SiteCountArray;
|
||||
for (I = 0; I <= IPVK_Last; I++) {
|
||||
uint16_t N = Data->NumValueSites[I];
|
||||
if (!N)
|
||||
continue;
|
||||
|
||||
NumValueKinds++;
|
||||
|
||||
RTRecord.NodesKind[I] = Nodes ? &Nodes[S] : INSTR_PROF_NULLPTR;
|
||||
for (J = 0; J < N; J++) {
|
||||
/* Compute value count for each site. */
|
||||
uint32_t C = 0;
|
||||
ValueProfNode *Site =
|
||||
Nodes ? RTRecord.NodesKind[I][J] : INSTR_PROF_NULLPTR;
|
||||
while (Site) {
|
||||
C++;
|
||||
Site = Site->Next;
|
||||
}
|
||||
if (C > UCHAR_MAX)
|
||||
C = UCHAR_MAX;
|
||||
RTRecord.SiteCountArray[I][J] = C;
|
||||
}
|
||||
S += N;
|
||||
}
|
||||
return NumValueKinds;
|
||||
}
|
||||
|
||||
static ValueProfNode *getNextNValueData(uint32_t VK, uint32_t Site,
|
||||
InstrProfValueData *Dst,
|
||||
ValueProfNode *StartNode, uint32_t N) {
|
||||
unsigned I;
|
||||
ValueProfNode *VNode = StartNode ? StartNode : RTRecord.NodesKind[VK][Site];
|
||||
for (I = 0; I < N; I++) {
|
||||
Dst[I].Value = VNode->Value;
|
||||
Dst[I].Count = VNode->Count;
|
||||
VNode = VNode->Next;
|
||||
}
|
||||
return VNode;
|
||||
}
|
||||
|
||||
static uint32_t getValueProfDataSizeWrapper(void) {
|
||||
return getValueProfDataSize(&RTRecordClosure);
|
||||
}
|
||||
|
||||
static uint32_t getNumValueDataForSiteWrapper(uint32_t VK, uint32_t S) {
|
||||
return getNumValueDataForSiteRT(&RTRecord, VK, S);
|
||||
}
|
||||
|
||||
static VPDataReaderType TheVPDataReader = {
|
||||
initializeValueProfRuntimeRecord, getValueProfRecordHeaderSize,
|
||||
getFirstValueProfRecord, getNumValueDataForSiteWrapper,
|
||||
getValueProfDataSizeWrapper, getNextNValueData};
|
||||
|
||||
COMPILER_RT_VISIBILITY VPDataReaderType *lprofGetVPDataReader() {
|
||||
return &TheVPDataReader;
|
||||
}
|
286
runtime/profile-rt/profile-rt-70/InstrProfilingWriter.c
Normal file
286
runtime/profile-rt/profile-rt-70/InstrProfilingWriter.c
Normal file
|
@ -0,0 +1,286 @@
|
|||
/*===- InstrProfilingWriter.c - Write instrumentation to a file or buffer -===*\
|
||||
|*
|
||||
|* The LLVM Compiler Infrastructure
|
||||
|*
|
||||
|* This file is distributed under the University of Illinois Open Source
|
||||
|* License. See LICENSE.TXT for details.
|
||||
|*
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
#include "InstrProfiling.h"
|
||||
#include "InstrProfilingInternal.h"
|
||||
#ifdef _MSC_VER
|
||||
/* For _alloca */
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
|
||||
#define INSTR_PROF_VALUE_PROF_DATA
|
||||
#include "InstrProfData.inc"
|
||||
|
||||
COMPILER_RT_VISIBILITY void (*FreeHook)(void *) = NULL;
|
||||
static ProfBufferIO TheBufferIO;
|
||||
#define VP_BUFFER_SIZE 8 * 1024
|
||||
static uint8_t BufferIOBuffer[VP_BUFFER_SIZE];
|
||||
static InstrProfValueData VPDataArray[16];
|
||||
static uint32_t VPDataArraySize = sizeof(VPDataArray) / sizeof(*VPDataArray);
|
||||
|
||||
COMPILER_RT_VISIBILITY uint8_t *DynamicBufferIOBuffer = 0;
|
||||
COMPILER_RT_VISIBILITY uint32_t VPBufferSize = 0;
|
||||
|
||||
/* The buffer writer is reponsponsible in keeping writer state
|
||||
* across the call.
|
||||
*/
|
||||
COMPILER_RT_VISIBILITY uint32_t lprofBufferWriter(ProfDataWriter *This,
|
||||
ProfDataIOVec *IOVecs,
|
||||
uint32_t NumIOVecs) {
|
||||
uint32_t I;
|
||||
char **Buffer = (char **)&This->WriterCtx;
|
||||
for (I = 0; I < NumIOVecs; I++) {
|
||||
size_t Length = IOVecs[I].ElmSize * IOVecs[I].NumElm;
|
||||
if (IOVecs[I].Data)
|
||||
memcpy(*Buffer, IOVecs[I].Data, Length);
|
||||
*Buffer += Length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void llvmInitBufferIO(ProfBufferIO *BufferIO, ProfDataWriter *FileWriter,
|
||||
uint8_t *Buffer, uint32_t BufferSz) {
|
||||
BufferIO->FileWriter = FileWriter;
|
||||
BufferIO->OwnFileWriter = 0;
|
||||
BufferIO->BufferStart = Buffer;
|
||||
BufferIO->BufferSz = BufferSz;
|
||||
BufferIO->CurOffset = 0;
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY ProfBufferIO *
|
||||
lprofCreateBufferIO(ProfDataWriter *FileWriter) {
|
||||
uint8_t *Buffer = DynamicBufferIOBuffer;
|
||||
uint32_t BufferSize = VPBufferSize;
|
||||
if (!Buffer) {
|
||||
Buffer = &BufferIOBuffer[0];
|
||||
BufferSize = sizeof(BufferIOBuffer);
|
||||
}
|
||||
llvmInitBufferIO(&TheBufferIO, FileWriter, Buffer, BufferSize);
|
||||
return &TheBufferIO;
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY void lprofDeleteBufferIO(ProfBufferIO *BufferIO) {
|
||||
if (BufferIO->OwnFileWriter)
|
||||
FreeHook(BufferIO->FileWriter);
|
||||
if (DynamicBufferIOBuffer) {
|
||||
FreeHook(DynamicBufferIOBuffer);
|
||||
DynamicBufferIOBuffer = 0;
|
||||
VPBufferSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY int
|
||||
lprofBufferIOWrite(ProfBufferIO *BufferIO, const uint8_t *Data, uint32_t Size) {
|
||||
/* Buffer is not large enough, it is time to flush. */
|
||||
if (Size + BufferIO->CurOffset > BufferIO->BufferSz) {
|
||||
if (lprofBufferIOFlush(BufferIO) != 0)
|
||||
return -1;
|
||||
}
|
||||
/* Special case, bypass the buffer completely. */
|
||||
ProfDataIOVec IO[] = {{Data, sizeof(uint8_t), Size}};
|
||||
if (Size > BufferIO->BufferSz) {
|
||||
if (BufferIO->FileWriter->Write(BufferIO->FileWriter, IO, 1))
|
||||
return -1;
|
||||
} else {
|
||||
/* Write the data to buffer */
|
||||
uint8_t *Buffer = BufferIO->BufferStart + BufferIO->CurOffset;
|
||||
ProfDataWriter BufferWriter;
|
||||
initBufferWriter(&BufferWriter, (char *)Buffer);
|
||||
lprofBufferWriter(&BufferWriter, IO, 1);
|
||||
BufferIO->CurOffset =
|
||||
(uint8_t *)BufferWriter.WriterCtx - BufferIO->BufferStart;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY int lprofBufferIOFlush(ProfBufferIO *BufferIO) {
|
||||
if (BufferIO->CurOffset) {
|
||||
ProfDataIOVec IO[] = {
|
||||
{BufferIO->BufferStart, sizeof(uint8_t), BufferIO->CurOffset}};
|
||||
if (BufferIO->FileWriter->Write(BufferIO->FileWriter, IO, 1))
|
||||
return -1;
|
||||
BufferIO->CurOffset = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Write out value profile data for function specified with \c Data.
|
||||
* The implementation does not use the method \c serializeValueProfData
|
||||
* which depends on dynamic memory allocation. In this implementation,
|
||||
* value profile data is written out to \c BufferIO piecemeal.
|
||||
*/
|
||||
static int writeOneValueProfData(ProfBufferIO *BufferIO,
|
||||
VPDataReaderType *VPDataReader,
|
||||
const __llvm_profile_data *Data) {
|
||||
unsigned I, NumValueKinds = 0;
|
||||
ValueProfData VPHeader;
|
||||
uint8_t *SiteCountArray[IPVK_Last + 1];
|
||||
|
||||
for (I = 0; I <= IPVK_Last; I++) {
|
||||
if (!Data->NumValueSites[I])
|
||||
SiteCountArray[I] = 0;
|
||||
else {
|
||||
uint32_t Sz =
|
||||
VPDataReader->GetValueProfRecordHeaderSize(Data->NumValueSites[I]) -
|
||||
offsetof(ValueProfRecord, SiteCountArray);
|
||||
/* Only use alloca for this small byte array to avoid excessive
|
||||
* stack growth. */
|
||||
SiteCountArray[I] = (uint8_t *)COMPILER_RT_ALLOCA(Sz);
|
||||
memset(SiteCountArray[I], 0, Sz);
|
||||
}
|
||||
}
|
||||
|
||||
/* If NumValueKinds returned is 0, there is nothing to write, report
|
||||
success and return. This should match the raw profile reader's behavior. */
|
||||
if (!(NumValueKinds = VPDataReader->InitRTRecord(Data, SiteCountArray)))
|
||||
return 0;
|
||||
|
||||
/* First write the header structure. */
|
||||
VPHeader.TotalSize = VPDataReader->GetValueProfDataSize();
|
||||
VPHeader.NumValueKinds = NumValueKinds;
|
||||
if (lprofBufferIOWrite(BufferIO, (const uint8_t *)&VPHeader,
|
||||
sizeof(ValueProfData)))
|
||||
return -1;
|
||||
|
||||
/* Make sure nothing else needs to be written before value profile
|
||||
* records. */
|
||||
if ((void *)VPDataReader->GetFirstValueProfRecord(&VPHeader) !=
|
||||
(void *)(&VPHeader + 1))
|
||||
return -1;
|
||||
|
||||
/* Write out the value profile record for each value kind
|
||||
* one by one. */
|
||||
for (I = 0; I <= IPVK_Last; I++) {
|
||||
uint32_t J;
|
||||
ValueProfRecord RecordHeader;
|
||||
/* The size of the value prof record header without counting the
|
||||
* site count array .*/
|
||||
uint32_t RecordHeaderSize = offsetof(ValueProfRecord, SiteCountArray);
|
||||
uint32_t SiteCountArraySize;
|
||||
|
||||
if (!Data->NumValueSites[I])
|
||||
continue;
|
||||
|
||||
/* Write out the record header. */
|
||||
RecordHeader.Kind = I;
|
||||
RecordHeader.NumValueSites = Data->NumValueSites[I];
|
||||
if (lprofBufferIOWrite(BufferIO, (const uint8_t *)&RecordHeader,
|
||||
RecordHeaderSize))
|
||||
return -1;
|
||||
|
||||
/* Write out the site value count array including padding space. */
|
||||
SiteCountArraySize =
|
||||
VPDataReader->GetValueProfRecordHeaderSize(Data->NumValueSites[I]) -
|
||||
RecordHeaderSize;
|
||||
if (lprofBufferIOWrite(BufferIO, SiteCountArray[I], SiteCountArraySize))
|
||||
return -1;
|
||||
|
||||
/* Write out the value profile data for each value site. */
|
||||
for (J = 0; J < Data->NumValueSites[I]; J++) {
|
||||
uint32_t NRead, NRemain;
|
||||
ValueProfNode *NextStartNode = 0;
|
||||
NRemain = VPDataReader->GetNumValueDataForSite(I, J);
|
||||
if (!NRemain)
|
||||
continue;
|
||||
/* Read and write out value data in small chunks till it is done. */
|
||||
do {
|
||||
NRead = (NRemain > VPDataArraySize ? VPDataArraySize : NRemain);
|
||||
NextStartNode =
|
||||
VPDataReader->GetValueData(I, /* ValueKind */
|
||||
J, /* Site */
|
||||
&VPDataArray[0], NextStartNode, NRead);
|
||||
if (lprofBufferIOWrite(BufferIO, (const uint8_t *)&VPDataArray[0],
|
||||
NRead * sizeof(InstrProfValueData)))
|
||||
return -1;
|
||||
NRemain -= NRead;
|
||||
} while (NRemain != 0);
|
||||
}
|
||||
}
|
||||
/* All done report success. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int writeValueProfData(ProfDataWriter *Writer,
|
||||
VPDataReaderType *VPDataReader,
|
||||
const __llvm_profile_data *DataBegin,
|
||||
const __llvm_profile_data *DataEnd) {
|
||||
ProfBufferIO *BufferIO;
|
||||
const __llvm_profile_data *DI = 0;
|
||||
|
||||
if (!VPDataReader)
|
||||
return 0;
|
||||
|
||||
BufferIO = lprofCreateBufferIO(Writer);
|
||||
|
||||
for (DI = DataBegin; DI < DataEnd; DI++) {
|
||||
if (writeOneValueProfData(BufferIO, VPDataReader, DI))
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (lprofBufferIOFlush(BufferIO) != 0)
|
||||
return -1;
|
||||
lprofDeleteBufferIO(BufferIO);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY int lprofWriteData(ProfDataWriter *Writer,
|
||||
VPDataReaderType *VPDataReader,
|
||||
int SkipNameDataWrite) {
|
||||
/* Match logic in __llvm_profile_write_buffer(). */
|
||||
const __llvm_profile_data *DataBegin = __llvm_profile_begin_data();
|
||||
const __llvm_profile_data *DataEnd = __llvm_profile_end_data();
|
||||
const uint64_t *CountersBegin = __llvm_profile_begin_counters();
|
||||
const uint64_t *CountersEnd = __llvm_profile_end_counters();
|
||||
const char *NamesBegin = __llvm_profile_begin_names();
|
||||
const char *NamesEnd = __llvm_profile_end_names();
|
||||
return lprofWriteDataImpl(Writer, DataBegin, DataEnd, CountersBegin,
|
||||
CountersEnd, VPDataReader, NamesBegin, NamesEnd,
|
||||
SkipNameDataWrite);
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY int
|
||||
lprofWriteDataImpl(ProfDataWriter *Writer, const __llvm_profile_data *DataBegin,
|
||||
const __llvm_profile_data *DataEnd,
|
||||
const uint64_t *CountersBegin, const uint64_t *CountersEnd,
|
||||
VPDataReaderType *VPDataReader, const char *NamesBegin,
|
||||
const char *NamesEnd, int SkipNameDataWrite) {
|
||||
|
||||
/* Calculate size of sections. */
|
||||
const uint64_t DataSize = __llvm_profile_get_data_size(DataBegin, DataEnd);
|
||||
const uint64_t CountersSize = CountersEnd - CountersBegin;
|
||||
const uint64_t NamesSize = NamesEnd - NamesBegin;
|
||||
const uint64_t Padding = __llvm_profile_get_num_padding_bytes(NamesSize);
|
||||
|
||||
/* Enough zeroes for padding. */
|
||||
const char Zeroes[sizeof(uint64_t)] = {0};
|
||||
|
||||
/* Create the header. */
|
||||
__llvm_profile_header Header;
|
||||
|
||||
if (!DataSize)
|
||||
return 0;
|
||||
|
||||
/* Initialize header structure. */
|
||||
#define INSTR_PROF_RAW_HEADER(Type, Name, Init) Header.Name = Init;
|
||||
#include "InstrProfData.inc"
|
||||
|
||||
/* Write the data. */
|
||||
ProfDataIOVec IOVec[] = {
|
||||
{&Header, sizeof(__llvm_profile_header), 1},
|
||||
{DataBegin, sizeof(__llvm_profile_data), DataSize},
|
||||
{CountersBegin, sizeof(uint64_t), CountersSize},
|
||||
{SkipNameDataWrite ? NULL : NamesBegin, sizeof(uint8_t), NamesSize},
|
||||
{Zeroes, sizeof(uint8_t), Padding}};
|
||||
if (Writer->Write(Writer, IOVec, sizeof(IOVec) / sizeof(*IOVec)))
|
||||
return -1;
|
||||
|
||||
return writeValueProfData(Writer, VPDataReader, DataBegin, DataEnd);
|
||||
}
|
131
runtime/profile-rt/profile-rt-70/WindowsMMap.c
Normal file
131
runtime/profile-rt/profile-rt-70/WindowsMMap.c
Normal file
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* This code is derived from uClibc (original license follows).
|
||||
* https://git.uclibc.org/uClibc/tree/utils/mmap-windows.c
|
||||
*/
|
||||
/* mmap() replacement for Windows
|
||||
*
|
||||
* Author: Mike Frysinger <vapier@gentoo.org>
|
||||
* Placed into the public domain
|
||||
*/
|
||||
|
||||
/* References:
|
||||
* CreateFileMapping: http://msdn.microsoft.com/en-us/library/aa366537(VS.85).aspx
|
||||
* CloseHandle: http://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx
|
||||
* MapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366761(VS.85).aspx
|
||||
* UnmapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366882(VS.85).aspx
|
||||
*/
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
#include "WindowsMMap.h"
|
||||
#include "InstrProfiling.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#ifdef __USE_FILE_OFFSET64
|
||||
# define DWORD_HI(x) (x >> 32)
|
||||
# define DWORD_LO(x) ((x) & 0xffffffff)
|
||||
#else
|
||||
# define DWORD_HI(x) (0)
|
||||
# define DWORD_LO(x) (x)
|
||||
#endif
|
||||
|
||||
COMPILER_RT_VISIBILITY
|
||||
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset)
|
||||
{
|
||||
if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))
|
||||
return MAP_FAILED;
|
||||
if (fd == -1) {
|
||||
if (!(flags & MAP_ANON) || offset)
|
||||
return MAP_FAILED;
|
||||
} else if (flags & MAP_ANON)
|
||||
return MAP_FAILED;
|
||||
|
||||
DWORD flProtect;
|
||||
if (prot & PROT_WRITE) {
|
||||
if (prot & PROT_EXEC)
|
||||
flProtect = PAGE_EXECUTE_READWRITE;
|
||||
else
|
||||
flProtect = PAGE_READWRITE;
|
||||
} else if (prot & PROT_EXEC) {
|
||||
if (prot & PROT_READ)
|
||||
flProtect = PAGE_EXECUTE_READ;
|
||||
else if (prot & PROT_EXEC)
|
||||
flProtect = PAGE_EXECUTE;
|
||||
} else
|
||||
flProtect = PAGE_READONLY;
|
||||
|
||||
off_t end = length + offset;
|
||||
HANDLE mmap_fd, h;
|
||||
if (fd == -1)
|
||||
mmap_fd = INVALID_HANDLE_VALUE;
|
||||
else
|
||||
mmap_fd = (HANDLE)_get_osfhandle(fd);
|
||||
h = CreateFileMapping(mmap_fd, NULL, flProtect, DWORD_HI(end), DWORD_LO(end), NULL);
|
||||
if (h == NULL)
|
||||
return MAP_FAILED;
|
||||
|
||||
DWORD dwDesiredAccess;
|
||||
if (prot & PROT_WRITE)
|
||||
dwDesiredAccess = FILE_MAP_WRITE;
|
||||
else
|
||||
dwDesiredAccess = FILE_MAP_READ;
|
||||
if (prot & PROT_EXEC)
|
||||
dwDesiredAccess |= FILE_MAP_EXECUTE;
|
||||
if (flags & MAP_PRIVATE)
|
||||
dwDesiredAccess |= FILE_MAP_COPY;
|
||||
void *ret = MapViewOfFile(h, dwDesiredAccess, DWORD_HI(offset), DWORD_LO(offset), length);
|
||||
if (ret == NULL) {
|
||||
CloseHandle(h);
|
||||
ret = MAP_FAILED;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY
|
||||
void munmap(void *addr, size_t length)
|
||||
{
|
||||
UnmapViewOfFile(addr);
|
||||
/* ruh-ro, we leaked handle from CreateFileMapping() ... */
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY
|
||||
int msync(void *addr, size_t length, int flags)
|
||||
{
|
||||
if (flags & MS_INVALIDATE)
|
||||
return -1; /* Not supported. */
|
||||
|
||||
/* Exactly one of MS_ASYNC or MS_SYNC must be specified. */
|
||||
switch (flags & (MS_ASYNC | MS_SYNC)) {
|
||||
case MS_SYNC:
|
||||
case MS_ASYNC:
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!FlushViewOfFile(addr, length))
|
||||
return -1;
|
||||
|
||||
if (flags & MS_SYNC) {
|
||||
/* FIXME: No longer have access to handle from CreateFileMapping(). */
|
||||
/*
|
||||
* if (!FlushFileBuffers(h))
|
||||
* return -1;
|
||||
*/
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
COMPILER_RT_VISIBILITY
|
||||
int flock(int fd, int operation)
|
||||
{
|
||||
return -1; /* Not supported. */
|
||||
}
|
||||
|
||||
#undef DWORD_HI
|
||||
#undef DWORD_LO
|
||||
|
||||
#endif /* _WIN32 */
|
59
runtime/profile-rt/profile-rt-70/WindowsMMap.h
Normal file
59
runtime/profile-rt/profile-rt-70/WindowsMMap.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*===- WindowsMMap.h - Support library for PGO instrumentation ------------===*\
|
||||
|*
|
||||
|* The LLVM Compiler Infrastructure
|
||||
|*
|
||||
|* This file is distributed under the University of Illinois Open Source
|
||||
|* License. See LICENSE.TXT for details.
|
||||
|*
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
#ifndef PROFILE_INSTRPROFILING_WINDOWS_MMAP_H
|
||||
#define PROFILE_INSTRPROFILING_WINDOWS_MMAP_H
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
#include <BaseTsd.h>
|
||||
#include <io.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
/*
|
||||
* mmap() flags
|
||||
*/
|
||||
#define PROT_READ 0x1
|
||||
#define PROT_WRITE 0x2
|
||||
#define PROT_EXEC 0x0
|
||||
|
||||
#define MAP_FILE 0x00
|
||||
#define MAP_SHARED 0x01
|
||||
#define MAP_PRIVATE 0x02
|
||||
#define MAP_ANONYMOUS 0x20
|
||||
#define MAP_ANON MAP_ANONYMOUS
|
||||
#define MAP_FAILED ((void *) -1)
|
||||
|
||||
/*
|
||||
* msync() flags
|
||||
*/
|
||||
#define MS_ASYNC 0x0001 /* return immediately */
|
||||
#define MS_INVALIDATE 0x0002 /* invalidate all cached data */
|
||||
#define MS_SYNC 0x0010 /* msync synchronously */
|
||||
|
||||
/*
|
||||
* flock() operations
|
||||
*/
|
||||
#define LOCK_SH 1 /* shared lock */
|
||||
#define LOCK_EX 2 /* exclusive lock */
|
||||
#define LOCK_NB 4 /* don't block when locking */
|
||||
#define LOCK_UN 8 /* unlock */
|
||||
|
||||
void *mmap(void *start, size_t length, int prot, int flags, int fd,
|
||||
off_t offset);
|
||||
|
||||
void munmap(void *addr, size_t length);
|
||||
|
||||
int msync(void *addr, size_t length, int flags);
|
||||
|
||||
int flock(int fd, int operation);
|
||||
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#endif /* PROFILE_INSTRPROFILING_WINDOWS_MMAP_H */
|
844
tools/ldc-profdata/llvm-profdata-7.0.cpp
Normal file
844
tools/ldc-profdata/llvm-profdata-7.0.cpp
Normal file
|
@ -0,0 +1,844 @@
|
|||
//===- llvm-profdata.cpp - LLVM profile data tool -------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// llvm-profdata merges .profdata files.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ADT/SmallSet.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/ProfileData/InstrProfReader.h"
|
||||
#include "llvm/ProfileData/InstrProfWriter.h"
|
||||
#include "llvm/ProfileData/ProfileCommon.h"
|
||||
#include "llvm/ProfileData/SampleProfReader.h"
|
||||
#include "llvm/ProfileData/SampleProfWriter.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/Errc.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/Format.h"
|
||||
#include "llvm/Support/ManagedStatic.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/PrettyStackTrace.h"
|
||||
#include "llvm/Support/Signals.h"
|
||||
#include "llvm/Support/ThreadPool.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
enum ProfileFormat { PF_None = 0, PF_Text, PF_Binary, PF_GCC };
|
||||
|
||||
static void warn(StringRef Prefix, Twine Message, std::string Whence = "",
|
||||
std::string Hint = "") {
|
||||
errs() << Prefix;
|
||||
if (!Whence.empty())
|
||||
errs() << Whence << ": ";
|
||||
errs() << Message << "\n";
|
||||
if (!Hint.empty())
|
||||
errs() << Hint << "\n";
|
||||
}
|
||||
|
||||
static void exitWithError(Twine Message, std::string Whence = "",
|
||||
std::string Hint = "") {
|
||||
warn("error: ", Message, Whence, Hint);
|
||||
::exit(1);
|
||||
}
|
||||
|
||||
static void exitWithError(Error E, StringRef Whence = "") {
|
||||
if (E.isA<InstrProfError>()) {
|
||||
handleAllErrors(std::move(E), [&](const InstrProfError &IPE) {
|
||||
instrprof_error instrError = IPE.get();
|
||||
StringRef Hint = "";
|
||||
if (instrError == instrprof_error::unrecognized_format) {
|
||||
// Hint for common error of forgetting -sample for sample profiles.
|
||||
Hint = "Perhaps you forgot to use the -sample option?";
|
||||
}
|
||||
exitWithError(IPE.message(), Whence, Hint);
|
||||
});
|
||||
}
|
||||
|
||||
exitWithError(toString(std::move(E)), Whence);
|
||||
}
|
||||
|
||||
static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") {
|
||||
exitWithError(EC.message(), Whence);
|
||||
}
|
||||
|
||||
namespace {
|
||||
enum ProfileKinds { instr, sample };
|
||||
}
|
||||
|
||||
static void handleMergeWriterError(Error E, StringRef WhenceFile = "",
|
||||
StringRef WhenceFunction = "",
|
||||
bool ShowHint = true) {
|
||||
if (!WhenceFile.empty())
|
||||
errs() << WhenceFile << ": ";
|
||||
if (!WhenceFunction.empty())
|
||||
errs() << WhenceFunction << ": ";
|
||||
|
||||
auto IPE = instrprof_error::success;
|
||||
E = handleErrors(std::move(E),
|
||||
[&IPE](std::unique_ptr<InstrProfError> E) -> Error {
|
||||
IPE = E->get();
|
||||
return Error(std::move(E));
|
||||
});
|
||||
errs() << toString(std::move(E)) << "\n";
|
||||
|
||||
if (ShowHint) {
|
||||
StringRef Hint = "";
|
||||
if (IPE != instrprof_error::success) {
|
||||
switch (IPE) {
|
||||
case instrprof_error::hash_mismatch:
|
||||
case instrprof_error::count_mismatch:
|
||||
case instrprof_error::value_site_count_mismatch:
|
||||
Hint = "Make sure that all profile data to be merged is generated "
|
||||
"from the same binary.";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Hint.empty())
|
||||
errs() << Hint << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
struct WeightedFile {
|
||||
std::string Filename;
|
||||
uint64_t Weight;
|
||||
};
|
||||
typedef SmallVector<WeightedFile, 5> WeightedFileVector;
|
||||
|
||||
/// Keep track of merged data and reported errors.
|
||||
struct WriterContext {
|
||||
std::mutex Lock;
|
||||
InstrProfWriter Writer;
|
||||
Error Err;
|
||||
std::string ErrWhence;
|
||||
std::mutex &ErrLock;
|
||||
SmallSet<instrprof_error, 4> &WriterErrorCodes;
|
||||
|
||||
WriterContext(bool IsSparse, std::mutex &ErrLock,
|
||||
SmallSet<instrprof_error, 4> &WriterErrorCodes)
|
||||
: Lock(), Writer(IsSparse), Err(Error::success()), ErrWhence(""),
|
||||
ErrLock(ErrLock), WriterErrorCodes(WriterErrorCodes) {}
|
||||
};
|
||||
|
||||
/// Determine whether an error is fatal for profile merging.
|
||||
static bool isFatalError(instrprof_error IPE) {
|
||||
switch (IPE) {
|
||||
default:
|
||||
return true;
|
||||
case instrprof_error::success:
|
||||
case instrprof_error::eof:
|
||||
case instrprof_error::unknown_function:
|
||||
case instrprof_error::hash_mismatch:
|
||||
case instrprof_error::count_mismatch:
|
||||
case instrprof_error::counter_overflow:
|
||||
case instrprof_error::value_site_count_mismatch:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Load an input into a writer context.
|
||||
static void loadInput(const WeightedFile &Input, WriterContext *WC) {
|
||||
std::unique_lock<std::mutex> CtxGuard{WC->Lock};
|
||||
|
||||
// If there's a pending hard error, don't do more work.
|
||||
if (WC->Err)
|
||||
return;
|
||||
|
||||
// Copy the filename, because llvm::ThreadPool copied the input "const
|
||||
// WeightedFile &" by value, making a reference to the filename within it
|
||||
// invalid outside of this packaged task.
|
||||
WC->ErrWhence = Input.Filename;
|
||||
|
||||
auto ReaderOrErr = InstrProfReader::create(Input.Filename);
|
||||
if (Error E = ReaderOrErr.takeError()) {
|
||||
// Skip the empty profiles by returning sliently.
|
||||
instrprof_error IPE = InstrProfError::take(std::move(E));
|
||||
if (IPE != instrprof_error::empty_raw_profile)
|
||||
WC->Err = make_error<InstrProfError>(IPE);
|
||||
return;
|
||||
}
|
||||
|
||||
auto Reader = std::move(ReaderOrErr.get());
|
||||
bool IsIRProfile = Reader->isIRLevelProfile();
|
||||
if (WC->Writer.setIsIRLevelProfile(IsIRProfile)) {
|
||||
WC->Err = make_error<StringError>(
|
||||
"Merge IR generated profile with Clang generated profile.",
|
||||
std::error_code());
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &I : *Reader) {
|
||||
const StringRef FuncName = I.Name;
|
||||
bool Reported = false;
|
||||
WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) {
|
||||
if (Reported) {
|
||||
consumeError(std::move(E));
|
||||
return;
|
||||
}
|
||||
Reported = true;
|
||||
// Only show hint the first time an error occurs.
|
||||
instrprof_error IPE = InstrProfError::take(std::move(E));
|
||||
std::unique_lock<std::mutex> ErrGuard{WC->ErrLock};
|
||||
bool firstTime = WC->WriterErrorCodes.insert(IPE).second;
|
||||
handleMergeWriterError(make_error<InstrProfError>(IPE), Input.Filename,
|
||||
FuncName, firstTime);
|
||||
});
|
||||
}
|
||||
if (Reader->hasError()) {
|
||||
if (Error E = Reader->getError()) {
|
||||
instrprof_error IPE = InstrProfError::take(std::move(E));
|
||||
if (isFatalError(IPE))
|
||||
WC->Err = make_error<InstrProfError>(IPE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Merge the \p Src writer context into \p Dst.
|
||||
static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) {
|
||||
// If we've already seen a hard error, continuing with the merge would
|
||||
// clobber it.
|
||||
if (Dst->Err || Src->Err)
|
||||
return;
|
||||
|
||||
bool Reported = false;
|
||||
Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) {
|
||||
if (Reported) {
|
||||
consumeError(std::move(E));
|
||||
return;
|
||||
}
|
||||
Reported = true;
|
||||
Dst->Err = std::move(E);
|
||||
});
|
||||
}
|
||||
|
||||
static void mergeInstrProfile(const WeightedFileVector &Inputs,
|
||||
StringRef OutputFilename,
|
||||
ProfileFormat OutputFormat, bool OutputSparse,
|
||||
unsigned NumThreads) {
|
||||
if (OutputFilename.compare("-") == 0)
|
||||
exitWithError("Cannot write indexed profdata format to stdout.");
|
||||
|
||||
if (OutputFormat != PF_Binary && OutputFormat != PF_Text)
|
||||
exitWithError("Unknown format is specified.");
|
||||
|
||||
std::error_code EC;
|
||||
raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::F_None);
|
||||
if (EC)
|
||||
exitWithErrorCode(EC, OutputFilename);
|
||||
|
||||
std::mutex ErrorLock;
|
||||
SmallSet<instrprof_error, 4> WriterErrorCodes;
|
||||
|
||||
// If NumThreads is not specified, auto-detect a good default.
|
||||
if (NumThreads == 0)
|
||||
NumThreads =
|
||||
std::min(hardware_concurrency(), unsigned((Inputs.size() + 1) / 2));
|
||||
|
||||
// Initialize the writer contexts.
|
||||
SmallVector<std::unique_ptr<WriterContext>, 4> Contexts;
|
||||
for (unsigned I = 0; I < NumThreads; ++I)
|
||||
Contexts.emplace_back(llvm::make_unique<WriterContext>(
|
||||
OutputSparse, ErrorLock, WriterErrorCodes));
|
||||
|
||||
if (NumThreads == 1) {
|
||||
for (const auto &Input : Inputs)
|
||||
loadInput(Input, Contexts[0].get());
|
||||
} else {
|
||||
ThreadPool Pool(NumThreads);
|
||||
|
||||
// Load the inputs in parallel (N/NumThreads serial steps).
|
||||
unsigned Ctx = 0;
|
||||
for (const auto &Input : Inputs) {
|
||||
Pool.async(loadInput, Input, Contexts[Ctx].get());
|
||||
Ctx = (Ctx + 1) % NumThreads;
|
||||
}
|
||||
Pool.wait();
|
||||
|
||||
// Merge the writer contexts together (~ lg(NumThreads) serial steps).
|
||||
unsigned Mid = Contexts.size() / 2;
|
||||
unsigned End = Contexts.size();
|
||||
assert(Mid > 0 && "Expected more than one context");
|
||||
do {
|
||||
for (unsigned I = 0; I < Mid; ++I)
|
||||
Pool.async(mergeWriterContexts, Contexts[I].get(),
|
||||
Contexts[I + Mid].get());
|
||||
Pool.wait();
|
||||
if (End & 1) {
|
||||
Pool.async(mergeWriterContexts, Contexts[0].get(),
|
||||
Contexts[End - 1].get());
|
||||
Pool.wait();
|
||||
}
|
||||
End = Mid;
|
||||
Mid /= 2;
|
||||
} while (Mid > 0);
|
||||
}
|
||||
|
||||
// Handle deferred hard errors encountered during merging.
|
||||
for (std::unique_ptr<WriterContext> &WC : Contexts) {
|
||||
if (!WC->Err)
|
||||
continue;
|
||||
if (!WC->Err.isA<InstrProfError>())
|
||||
exitWithError(std::move(WC->Err), WC->ErrWhence);
|
||||
|
||||
instrprof_error IPE = InstrProfError::take(std::move(WC->Err));
|
||||
if (isFatalError(IPE))
|
||||
exitWithError(make_error<InstrProfError>(IPE), WC->ErrWhence);
|
||||
else
|
||||
warn("warning: ", toString(make_error<InstrProfError>(IPE)),
|
||||
WC->ErrWhence);
|
||||
}
|
||||
|
||||
InstrProfWriter &Writer = Contexts[0]->Writer;
|
||||
if (OutputFormat == PF_Text) {
|
||||
if (Error E = Writer.writeText(Output))
|
||||
exitWithError(std::move(E));
|
||||
} else {
|
||||
Writer.write(Output);
|
||||
}
|
||||
}
|
||||
|
||||
static sampleprof::SampleProfileFormat FormatMap[] = {
|
||||
sampleprof::SPF_None, sampleprof::SPF_Text, sampleprof::SPF_Binary,
|
||||
sampleprof::SPF_GCC};
|
||||
|
||||
static void mergeSampleProfile(const WeightedFileVector &Inputs,
|
||||
StringRef OutputFilename,
|
||||
ProfileFormat OutputFormat) {
|
||||
using namespace sampleprof;
|
||||
auto WriterOrErr =
|
||||
SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]);
|
||||
if (std::error_code EC = WriterOrErr.getError())
|
||||
exitWithErrorCode(EC, OutputFilename);
|
||||
|
||||
auto Writer = std::move(WriterOrErr.get());
|
||||
StringMap<FunctionSamples> ProfileMap;
|
||||
SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers;
|
||||
LLVMContext Context;
|
||||
for (const auto &Input : Inputs) {
|
||||
auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context);
|
||||
if (std::error_code EC = ReaderOrErr.getError())
|
||||
exitWithErrorCode(EC, Input.Filename);
|
||||
|
||||
// We need to keep the readers around until after all the files are
|
||||
// read so that we do not lose the function names stored in each
|
||||
// reader's memory. The function names are needed to write out the
|
||||
// merged profile map.
|
||||
Readers.push_back(std::move(ReaderOrErr.get()));
|
||||
const auto Reader = Readers.back().get();
|
||||
if (std::error_code EC = Reader->read())
|
||||
exitWithErrorCode(EC, Input.Filename);
|
||||
|
||||
StringMap<FunctionSamples> &Profiles = Reader->getProfiles();
|
||||
for (StringMap<FunctionSamples>::iterator I = Profiles.begin(),
|
||||
E = Profiles.end();
|
||||
I != E; ++I) {
|
||||
StringRef FName = I->first();
|
||||
FunctionSamples &Samples = I->second;
|
||||
sampleprof_error Result = ProfileMap[FName].merge(Samples, Input.Weight);
|
||||
if (Result != sampleprof_error::success) {
|
||||
std::error_code EC = make_error_code(Result);
|
||||
handleMergeWriterError(errorCodeToError(EC), Input.Filename, FName);
|
||||
}
|
||||
}
|
||||
}
|
||||
Writer->write(ProfileMap);
|
||||
}
|
||||
|
||||
static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) {
|
||||
StringRef WeightStr, FileName;
|
||||
std::tie(WeightStr, FileName) = WeightedFilename.split(',');
|
||||
|
||||
uint64_t Weight;
|
||||
if (WeightStr.getAsInteger(10, Weight) || Weight < 1)
|
||||
exitWithError("Input weight must be a positive integer.");
|
||||
|
||||
return {FileName, Weight};
|
||||
}
|
||||
|
||||
static std::unique_ptr<MemoryBuffer>
|
||||
getInputFilenamesFileBuf(const StringRef &InputFilenamesFile) {
|
||||
if (InputFilenamesFile == "")
|
||||
return {};
|
||||
|
||||
auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFilenamesFile);
|
||||
if (!BufOrError)
|
||||
exitWithErrorCode(BufOrError.getError(), InputFilenamesFile);
|
||||
|
||||
return std::move(*BufOrError);
|
||||
}
|
||||
|
||||
static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) {
|
||||
StringRef Filename = WF.Filename;
|
||||
uint64_t Weight = WF.Weight;
|
||||
|
||||
// If it's STDIN just pass it on.
|
||||
if (Filename == "-") {
|
||||
WNI.push_back({Filename, Weight});
|
||||
return;
|
||||
}
|
||||
|
||||
llvm::sys::fs::file_status Status;
|
||||
llvm::sys::fs::status(Filename, Status);
|
||||
if (!llvm::sys::fs::exists(Status))
|
||||
exitWithErrorCode(make_error_code(errc::no_such_file_or_directory),
|
||||
Filename);
|
||||
// If it's a source file, collect it.
|
||||
if (llvm::sys::fs::is_regular_file(Status)) {
|
||||
WNI.push_back({Filename, Weight});
|
||||
return;
|
||||
}
|
||||
|
||||
if (llvm::sys::fs::is_directory(Status)) {
|
||||
std::error_code EC;
|
||||
for (llvm::sys::fs::recursive_directory_iterator F(Filename, EC), E;
|
||||
F != E && !EC; F.increment(EC)) {
|
||||
if (llvm::sys::fs::is_regular_file(F->path())) {
|
||||
addWeightedInput(WNI, {F->path(), Weight});
|
||||
}
|
||||
}
|
||||
if (EC)
|
||||
exitWithErrorCode(EC, Filename);
|
||||
}
|
||||
}
|
||||
|
||||
static void parseInputFilenamesFile(MemoryBuffer *Buffer,
|
||||
WeightedFileVector &WFV) {
|
||||
if (!Buffer)
|
||||
return;
|
||||
|
||||
SmallVector<StringRef, 8> Entries;
|
||||
StringRef Data = Buffer->getBuffer();
|
||||
Data.split(Entries, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
|
||||
for (const StringRef &FileWeightEntry : Entries) {
|
||||
StringRef SanitizedEntry = FileWeightEntry.trim(" \t\v\f\r");
|
||||
// Skip comments.
|
||||
if (SanitizedEntry.startswith("#"))
|
||||
continue;
|
||||
// If there's no comma, it's an unweighted profile.
|
||||
else if (SanitizedEntry.find(',') == StringRef::npos)
|
||||
addWeightedInput(WFV, {SanitizedEntry, 1});
|
||||
else
|
||||
addWeightedInput(WFV, parseWeightedFile(SanitizedEntry));
|
||||
}
|
||||
}
|
||||
|
||||
static int merge_main(int argc, const char *argv[]) {
|
||||
cl::list<std::string> InputFilenames(cl::Positional,
|
||||
cl::desc("<filename...>"));
|
||||
cl::list<std::string> WeightedInputFilenames("weighted-input",
|
||||
cl::desc("<weight>,<filename>"));
|
||||
cl::opt<std::string> InputFilenamesFile(
|
||||
"input-files", cl::init(""),
|
||||
cl::desc("Path to file containing newline-separated "
|
||||
"[<weight>,]<filename> entries"));
|
||||
cl::alias InputFilenamesFileA("f", cl::desc("Alias for --input-files"),
|
||||
cl::aliasopt(InputFilenamesFile));
|
||||
cl::opt<bool> DumpInputFileList(
|
||||
"dump-input-file-list", cl::init(false), cl::Hidden,
|
||||
cl::desc("Dump the list of input files and their weights, then exit"));
|
||||
cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
|
||||
cl::init("-"), cl::Required,
|
||||
cl::desc("Output file"));
|
||||
cl::alias OutputFilenameA("o", cl::desc("Alias for --output"),
|
||||
cl::aliasopt(OutputFilename));
|
||||
cl::opt<ProfileKinds> ProfileKind(
|
||||
cl::desc("Profile kind:"), cl::init(instr),
|
||||
cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
|
||||
clEnumVal(sample, "Sample profile")));
|
||||
cl::opt<ProfileFormat> OutputFormat(
|
||||
cl::desc("Format of output profile"), cl::init(PF_Binary),
|
||||
cl::values(clEnumValN(PF_Binary, "binary", "Binary encoding (default)"),
|
||||
clEnumValN(PF_Text, "text", "Text encoding"),
|
||||
clEnumValN(PF_GCC, "gcc",
|
||||
"GCC encoding (only meaningful for -sample)")));
|
||||
cl::opt<bool> OutputSparse("sparse", cl::init(false),
|
||||
cl::desc("Generate a sparse profile (only meaningful for -instr)"));
|
||||
cl::opt<unsigned> NumThreads(
|
||||
"num-threads", cl::init(0),
|
||||
cl::desc("Number of merge threads to use (default: autodetect)"));
|
||||
cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"),
|
||||
cl::aliasopt(NumThreads));
|
||||
|
||||
cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n");
|
||||
|
||||
WeightedFileVector WeightedInputs;
|
||||
for (StringRef Filename : InputFilenames)
|
||||
addWeightedInput(WeightedInputs, {Filename, 1});
|
||||
for (StringRef WeightedFilename : WeightedInputFilenames)
|
||||
addWeightedInput(WeightedInputs, parseWeightedFile(WeightedFilename));
|
||||
|
||||
// Make sure that the file buffer stays alive for the duration of the
|
||||
// weighted input vector's lifetime.
|
||||
auto Buffer = getInputFilenamesFileBuf(InputFilenamesFile);
|
||||
parseInputFilenamesFile(Buffer.get(), WeightedInputs);
|
||||
|
||||
if (WeightedInputs.empty())
|
||||
exitWithError("No input files specified. See " +
|
||||
sys::path::filename(argv[0]) + " -help");
|
||||
|
||||
if (DumpInputFileList) {
|
||||
for (auto &WF : WeightedInputs)
|
||||
outs() << WF.Weight << "," << WF.Filename << "\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ProfileKind == instr)
|
||||
mergeInstrProfile(WeightedInputs, OutputFilename, OutputFormat,
|
||||
OutputSparse, NumThreads);
|
||||
else
|
||||
mergeSampleProfile(WeightedInputs, OutputFilename, OutputFormat);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct ValueSitesStats {
|
||||
ValueSitesStats()
|
||||
: TotalNumValueSites(0), TotalNumValueSitesWithValueProfile(0),
|
||||
TotalNumValues(0) {}
|
||||
uint64_t TotalNumValueSites;
|
||||
uint64_t TotalNumValueSitesWithValueProfile;
|
||||
uint64_t TotalNumValues;
|
||||
std::vector<unsigned> ValueSitesHistogram;
|
||||
} ValueSitesStats;
|
||||
|
||||
static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK,
|
||||
ValueSitesStats &Stats, raw_fd_ostream &OS,
|
||||
InstrProfSymtab *Symtab) {
|
||||
uint32_t NS = Func.getNumValueSites(VK);
|
||||
Stats.TotalNumValueSites += NS;
|
||||
for (size_t I = 0; I < NS; ++I) {
|
||||
uint32_t NV = Func.getNumValueDataForSite(VK, I);
|
||||
std::unique_ptr<InstrProfValueData[]> VD = Func.getValueForSite(VK, I);
|
||||
Stats.TotalNumValues += NV;
|
||||
if (NV) {
|
||||
Stats.TotalNumValueSitesWithValueProfile++;
|
||||
if (NV > Stats.ValueSitesHistogram.size())
|
||||
Stats.ValueSitesHistogram.resize(NV, 0);
|
||||
Stats.ValueSitesHistogram[NV - 1]++;
|
||||
}
|
||||
for (uint32_t V = 0; V < NV; V++) {
|
||||
OS << "\t[ " << I << ", ";
|
||||
if (Symtab == nullptr)
|
||||
OS << VD[V].Value;
|
||||
else
|
||||
OS << Symtab->getFuncName(VD[V].Value);
|
||||
OS << ", " << VD[V].Count << " ]\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void showValueSitesStats(raw_fd_ostream &OS, uint32_t VK,
|
||||
ValueSitesStats &Stats) {
|
||||
OS << " Total number of sites: " << Stats.TotalNumValueSites << "\n";
|
||||
OS << " Total number of sites with values: "
|
||||
<< Stats.TotalNumValueSitesWithValueProfile << "\n";
|
||||
OS << " Total number of profiled values: " << Stats.TotalNumValues << "\n";
|
||||
|
||||
OS << " Value sites histogram:\n\tNumTargets, SiteCount\n";
|
||||
for (unsigned I = 0; I < Stats.ValueSitesHistogram.size(); I++) {
|
||||
if (Stats.ValueSitesHistogram[I] > 0)
|
||||
OS << "\t" << I + 1 << ", " << Stats.ValueSitesHistogram[I] << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
static int showInstrProfile(const std::string &Filename, bool ShowCounts,
|
||||
uint32_t TopN, bool ShowIndirectCallTargets,
|
||||
bool ShowMemOPSizes, bool ShowDetailedSummary,
|
||||
std::vector<uint32_t> DetailedSummaryCutoffs,
|
||||
bool ShowAllFunctions,
|
||||
const std::string &ShowFunction, bool TextFormat,
|
||||
raw_fd_ostream &OS) {
|
||||
auto ReaderOrErr = InstrProfReader::create(Filename);
|
||||
std::vector<uint32_t> Cutoffs = std::move(DetailedSummaryCutoffs);
|
||||
if (ShowDetailedSummary && Cutoffs.empty()) {
|
||||
Cutoffs = {800000, 900000, 950000, 990000, 999000, 999900, 999990};
|
||||
}
|
||||
InstrProfSummaryBuilder Builder(std::move(Cutoffs));
|
||||
if (Error E = ReaderOrErr.takeError())
|
||||
exitWithError(std::move(E), Filename);
|
||||
|
||||
auto Reader = std::move(ReaderOrErr.get());
|
||||
bool IsIRInstr = Reader->isIRLevelProfile();
|
||||
size_t ShownFunctions = 0;
|
||||
int NumVPKind = IPVK_Last - IPVK_First + 1;
|
||||
std::vector<ValueSitesStats> VPStats(NumVPKind);
|
||||
|
||||
auto MinCmp = [](const std::pair<std::string, uint64_t> &v1,
|
||||
const std::pair<std::string, uint64_t> &v2) {
|
||||
return v1.second > v2.second;
|
||||
};
|
||||
|
||||
std::priority_queue<std::pair<std::string, uint64_t>,
|
||||
std::vector<std::pair<std::string, uint64_t>>,
|
||||
decltype(MinCmp)>
|
||||
HottestFuncs(MinCmp);
|
||||
|
||||
for (const auto &Func : *Reader) {
|
||||
bool Show =
|
||||
ShowAllFunctions || (!ShowFunction.empty() &&
|
||||
Func.Name.find(ShowFunction) != Func.Name.npos);
|
||||
|
||||
bool doTextFormatDump = (Show && ShowCounts && TextFormat);
|
||||
|
||||
if (doTextFormatDump) {
|
||||
InstrProfSymtab &Symtab = Reader->getSymtab();
|
||||
InstrProfWriter::writeRecordInText(Func.Name, Func.Hash, Func, Symtab,
|
||||
OS);
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(Func.Counts.size() > 0 && "function missing entry counter");
|
||||
Builder.addRecord(Func);
|
||||
|
||||
if (TopN) {
|
||||
uint64_t FuncMax = 0;
|
||||
for (size_t I = 0, E = Func.Counts.size(); I < E; ++I)
|
||||
FuncMax = std::max(FuncMax, Func.Counts[I]);
|
||||
|
||||
if (HottestFuncs.size() == TopN) {
|
||||
if (HottestFuncs.top().second < FuncMax) {
|
||||
HottestFuncs.pop();
|
||||
HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax));
|
||||
}
|
||||
} else
|
||||
HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax));
|
||||
}
|
||||
|
||||
if (Show) {
|
||||
|
||||
if (!ShownFunctions)
|
||||
OS << "Counters:\n";
|
||||
|
||||
++ShownFunctions;
|
||||
|
||||
OS << " " << Func.Name << ":\n"
|
||||
<< " Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n"
|
||||
<< " Counters: " << Func.Counts.size() << "\n";
|
||||
if (!IsIRInstr)
|
||||
OS << " Function count: " << Func.Counts[0] << "\n";
|
||||
|
||||
if (ShowIndirectCallTargets)
|
||||
OS << " Indirect Call Site Count: "
|
||||
<< Func.getNumValueSites(IPVK_IndirectCallTarget) << "\n";
|
||||
|
||||
uint32_t NumMemOPCalls = Func.getNumValueSites(IPVK_MemOPSize);
|
||||
if (ShowMemOPSizes && NumMemOPCalls > 0)
|
||||
OS << " Number of Memory Intrinsics Calls: " << NumMemOPCalls
|
||||
<< "\n";
|
||||
|
||||
if (ShowCounts) {
|
||||
OS << " Block counts: [";
|
||||
size_t Start = (IsIRInstr ? 0 : 1);
|
||||
for (size_t I = Start, E = Func.Counts.size(); I < E; ++I) {
|
||||
OS << (I == Start ? "" : ", ") << Func.Counts[I];
|
||||
}
|
||||
OS << "]\n";
|
||||
}
|
||||
|
||||
if (ShowIndirectCallTargets) {
|
||||
OS << " Indirect Target Results:\n";
|
||||
traverseAllValueSites(Func, IPVK_IndirectCallTarget,
|
||||
VPStats[IPVK_IndirectCallTarget], OS,
|
||||
&(Reader->getSymtab()));
|
||||
}
|
||||
|
||||
if (ShowMemOPSizes && NumMemOPCalls > 0) {
|
||||
OS << " Memory Intrinsic Size Results:\n";
|
||||
traverseAllValueSites(Func, IPVK_MemOPSize, VPStats[IPVK_MemOPSize], OS,
|
||||
nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Reader->hasError())
|
||||
exitWithError(Reader->getError(), Filename);
|
||||
|
||||
if (ShowCounts && TextFormat)
|
||||
return 0;
|
||||
std::unique_ptr<ProfileSummary> PS(Builder.getSummary());
|
||||
OS << "Instrumentation level: "
|
||||
<< (Reader->isIRLevelProfile() ? "IR" : "Front-end") << "\n";
|
||||
if (ShowAllFunctions || !ShowFunction.empty())
|
||||
OS << "Functions shown: " << ShownFunctions << "\n";
|
||||
OS << "Total functions: " << PS->getNumFunctions() << "\n";
|
||||
OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n";
|
||||
OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n";
|
||||
|
||||
if (TopN) {
|
||||
std::vector<std::pair<std::string, uint64_t>> SortedHottestFuncs;
|
||||
while (!HottestFuncs.empty()) {
|
||||
SortedHottestFuncs.emplace_back(HottestFuncs.top());
|
||||
HottestFuncs.pop();
|
||||
}
|
||||
OS << "Top " << TopN
|
||||
<< " functions with the largest internal block counts: \n";
|
||||
for (auto &hotfunc : llvm::reverse(SortedHottestFuncs))
|
||||
OS << " " << hotfunc.first << ", max count = " << hotfunc.second << "\n";
|
||||
}
|
||||
|
||||
if (ShownFunctions && ShowIndirectCallTargets) {
|
||||
OS << "Statistics for indirect call sites profile:\n";
|
||||
showValueSitesStats(OS, IPVK_IndirectCallTarget,
|
||||
VPStats[IPVK_IndirectCallTarget]);
|
||||
}
|
||||
|
||||
if (ShownFunctions && ShowMemOPSizes) {
|
||||
OS << "Statistics for memory intrinsic calls sizes profile:\n";
|
||||
showValueSitesStats(OS, IPVK_MemOPSize, VPStats[IPVK_MemOPSize]);
|
||||
}
|
||||
|
||||
if (ShowDetailedSummary) {
|
||||
OS << "Detailed summary:\n";
|
||||
OS << "Total number of blocks: " << PS->getNumCounts() << "\n";
|
||||
OS << "Total count: " << PS->getTotalCount() << "\n";
|
||||
for (auto Entry : PS->getDetailedSummary()) {
|
||||
OS << Entry.NumCounts << " blocks with count >= " << Entry.MinCount
|
||||
<< " account for "
|
||||
<< format("%0.6g", (float)Entry.Cutoff / ProfileSummary::Scale * 100)
|
||||
<< " percentage of the total counts.\n";
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int showSampleProfile(const std::string &Filename, bool ShowCounts,
|
||||
bool ShowAllFunctions,
|
||||
const std::string &ShowFunction,
|
||||
raw_fd_ostream &OS) {
|
||||
using namespace sampleprof;
|
||||
LLVMContext Context;
|
||||
auto ReaderOrErr = SampleProfileReader::create(Filename, Context);
|
||||
if (std::error_code EC = ReaderOrErr.getError())
|
||||
exitWithErrorCode(EC, Filename);
|
||||
|
||||
auto Reader = std::move(ReaderOrErr.get());
|
||||
if (std::error_code EC = Reader->read())
|
||||
exitWithErrorCode(EC, Filename);
|
||||
|
||||
if (ShowAllFunctions || ShowFunction.empty())
|
||||
Reader->dump(OS);
|
||||
else
|
||||
Reader->dumpFunctionProfile(ShowFunction, OS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int show_main(int argc, const char *argv[]) {
|
||||
cl::opt<std::string> Filename(cl::Positional, cl::Required,
|
||||
cl::desc("<profdata-file>"));
|
||||
|
||||
cl::opt<bool> ShowCounts("counts", cl::init(false),
|
||||
cl::desc("Show counter values for shown functions"));
|
||||
cl::opt<bool> TextFormat(
|
||||
"text", cl::init(false),
|
||||
cl::desc("Show instr profile data in text dump format"));
|
||||
cl::opt<bool> ShowIndirectCallTargets(
|
||||
"ic-targets", cl::init(false),
|
||||
cl::desc("Show indirect call site target values for shown functions"));
|
||||
cl::opt<bool> ShowMemOPSizes(
|
||||
"memop-sizes", cl::init(false),
|
||||
cl::desc("Show the profiled sizes of the memory intrinsic calls "
|
||||
"for shown functions"));
|
||||
cl::opt<bool> ShowDetailedSummary("detailed-summary", cl::init(false),
|
||||
cl::desc("Show detailed profile summary"));
|
||||
cl::list<uint32_t> DetailedSummaryCutoffs(
|
||||
cl::CommaSeparated, "detailed-summary-cutoffs",
|
||||
cl::desc(
|
||||
"Cutoff percentages (times 10000) for generating detailed summary"),
|
||||
cl::value_desc("800000,901000,999999"));
|
||||
cl::opt<bool> ShowAllFunctions("all-functions", cl::init(false),
|
||||
cl::desc("Details for every function"));
|
||||
cl::opt<std::string> ShowFunction("function",
|
||||
cl::desc("Details for matching functions"));
|
||||
|
||||
cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
|
||||
cl::init("-"), cl::desc("Output file"));
|
||||
cl::alias OutputFilenameA("o", cl::desc("Alias for --output"),
|
||||
cl::aliasopt(OutputFilename));
|
||||
cl::opt<ProfileKinds> ProfileKind(
|
||||
cl::desc("Profile kind:"), cl::init(instr),
|
||||
cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
|
||||
clEnumVal(sample, "Sample profile")));
|
||||
cl::opt<uint32_t> TopNFunctions(
|
||||
"topn", cl::init(0),
|
||||
cl::desc("Show the list of functions with the largest internal counts"));
|
||||
|
||||
cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n");
|
||||
|
||||
if (OutputFilename.empty())
|
||||
OutputFilename = "-";
|
||||
|
||||
std::error_code EC;
|
||||
raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::F_Text);
|
||||
if (EC)
|
||||
exitWithErrorCode(EC, OutputFilename);
|
||||
|
||||
if (ShowAllFunctions && !ShowFunction.empty())
|
||||
errs() << "warning: -function argument ignored: showing all functions\n";
|
||||
|
||||
std::vector<uint32_t> Cutoffs(DetailedSummaryCutoffs.begin(),
|
||||
DetailedSummaryCutoffs.end());
|
||||
if (ProfileKind == instr)
|
||||
return showInstrProfile(Filename, ShowCounts, TopNFunctions,
|
||||
ShowIndirectCallTargets, ShowMemOPSizes,
|
||||
ShowDetailedSummary, DetailedSummaryCutoffs,
|
||||
ShowAllFunctions, ShowFunction, TextFormat, OS);
|
||||
else
|
||||
return showSampleProfile(Filename, ShowCounts, ShowAllFunctions,
|
||||
ShowFunction, OS);
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
// Print a stack trace if we signal out.
|
||||
sys::PrintStackTraceOnErrorSignal(argv[0]);
|
||||
PrettyStackTraceProgram X(argc, argv);
|
||||
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
|
||||
|
||||
StringRef ProgName(sys::path::filename(argv[0]));
|
||||
if (argc > 1) {
|
||||
int (*func)(int, const char *[]) = nullptr;
|
||||
|
||||
if (strcmp(argv[1], "merge") == 0)
|
||||
func = merge_main;
|
||||
else if (strcmp(argv[1], "show") == 0)
|
||||
func = show_main;
|
||||
|
||||
if (func) {
|
||||
std::string Invocation(ProgName.str() + " " + argv[1]);
|
||||
argv[1] = Invocation.c_str();
|
||||
return func(argc - 1, argv + 1);
|
||||
}
|
||||
|
||||
if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-help") == 0 ||
|
||||
strcmp(argv[1], "--help") == 0) {
|
||||
|
||||
errs() << "OVERVIEW: LLVM profile data tools\n\n"
|
||||
<< "USAGE: " << ProgName << " <command> [args...]\n"
|
||||
<< "USAGE: " << ProgName << " <command> -help\n\n"
|
||||
<< "See each individual command --help for more details.\n"
|
||||
<< "Available commands: merge, show\n";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc < 2)
|
||||
errs() << ProgName << ": No command specified!\n";
|
||||
else
|
||||
errs() << ProgName << ": Unknown command!\n";
|
||||
|
||||
errs() << "USAGE: " << ProgName << " <merge|show> [args...]\n";
|
||||
return 1;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue