blob: 14a5132e4f2ec78c397a8215dec18c0c7467a5a0 [file] [log] [blame]
/* Performance monitoring unit (PMU) profiler. If available, use an
external tool to collect hardware performance counter data and
write it in the .gcda files.
Copyright (C) 2011. Free Software Foundation, Inc.
Contributed by Sharad Singhai <singhai@google.com>.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
Under Section 7 of GPL version 3, you are granted additional
permissions described in the GCC Runtime Library Exception, version
3.1, as published by the Free Software Foundation.
You should have received a copy of the GNU General Public License and
a copy of the GCC Runtime Library Exception along with this program;
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
<http://www.gnu.org/licenses/>. */
#include "tconfig.h"
#include "tsystem.h"
#include "coretypes.h"
#include "tm.h"
#if (defined (__x86_64__) || defined (__i386__))
#include "cpuid.h"
#endif
#if defined(inhibit_libc)
#define IN_LIBGCOV (-1)
#else
#include <stdio.h>
#include <stdlib.h>
#define IN_LIBGCOV 1
#if defined(L_gcov)
#define GCOV_LINKAGE /* nothing */
#endif
#endif
#include "gcov-io.h"
#ifdef TARGET_POSIX_IO
#include <fcntl.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#endif
#if defined(inhibit_libc)
#else
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define XNEWVEC(type,ne) (type *)calloc((ne),sizeof(type))
#define XNEW(type) (type *)malloc(sizeof(type))
#define XDELETEVEC(p) free(p)
#define XDELETE(p) free(p)
#define PFMON_CMD "/usr/bin/pfmon"
#define ADDR2LINE_CMD "/usr/bin/addr2line"
#define PMU_TOOL_MAX_ARGS (20)
static char default_addr2line[] = "??:0";
static const char pfmon_ll_header[] = "# counts %self %cum "
"<10 <32 <64 <256 <1024 >=1024 %wself "
"code addr symbol\n";
static const char pfmon_bm_header[] =
"# counts %self %cum code addr symbol\n";
const char *pfmon_intel_ll_args[PMU_TOOL_MAX_ARGS] = {
PFMON_CMD,
"--aggregate-results",
"--follow-all",
"--with-header",
"--smpl-module=pebs-ll",
"--ld-lat-threshold=4",
"--pebs-ll-dcmiss-code",
"--resolve-addresses",
"-emem_inst_retired:LATENCY_ABOVE_THRESHOLD",
"--long-smpl-periods=10000",
0 /* terminating NULL must be present */
};
const char *pfmon_amd_ll_args[PMU_TOOL_MAX_ARGS] = {
PFMON_CMD,
"--aggregate-results",
"--follow-all",
"-uk",
"--with-header",
"--smpl-module=ibs",
"--resolve-addresses",
"-eibsop_event:uops",
"--ibs-dcmiss-code",
"--long-smpl-periods=0xffff0",
0 /* terminating NULL must be present */
};
const char *pfmon_intel_brm_args[PMU_TOOL_MAX_ARGS] = {
PFMON_CMD,
"--aggregate-results",
"--follow-all",
"--with-header",
"--resolve-addresses",
"-eMISPREDICTED_BRANCH_RETIRED",
"--long-smpl-periods=10000",
0 /* terminating NULL must be present */
};
const char *pfmon_amd_brm_args[PMU_TOOL_MAX_ARGS] = {
PFMON_CMD,
"--aggregate-results",
"--follow-all",
"--with-header",
"--resolve-addresses",
"-eRETIRED_MISPREDICTED_BRANCH_INSTRUCTIONS",
"--long-smpl-periods=10000",
0 /* terminating NULL must be present */
};
const char *addr2line_args[PMU_TOOL_MAX_ARGS] = {
ADDR2LINE_CMD,
"-e",
0 /* terminating NULL must be present */
};
enum pmu_tool_type
{
PTT_PFMON,
PTT_LAST
};
enum pmu_event_type
{
PET_INTEL_LOAD_LATENCY,
PET_AMD_LOAD_LATENCY,
PET_INTEL_BRANCH_MISPREDICT,
PET_AMD_BRANCH_MISPREDICT,
PET_LAST
};
typedef struct pmu_tool_fns {
const char *name; /* name of the pmu tool */
/* pmu tool commandline argument. */
const char **arg_array;
/* Initialize pmu module. */
void *(*init_pmu_module) (void);
/* Start profililing. */
void (*start_pmu_module) (pid_t ppid, char *tmpfile, const char **args);
/* Stop profililing. */
void (*stop_pmu_module) (void);
/* How to parse the output generated by the PMU tool. */
int (*parse_pmu_output) (char *filename, void *pmu_data);
/* How to write parsed pmu data into gcda file. */
void (*gcov_write_pmu_data) (void *data);
/* How to cleanup any data structure created during parsing. */
void (*cleanup_pmu_data) (void *data);
/* How to initialize symbolizer for the PPID. */
int (*start_symbolizer) (pid_t ppid);
void (*end_symbolizer) (void);
char *(*symbolize) (void *addr);
} pmu_tool_fns;
enum pmu_state
{
PMU_NONE, /* Not configurated at all. */
PMU_INITIALIZED, /* Configured and initialized. */
PMU_ERROR, /* Configuration error. Cannot recover. */
PMU_ON, /* Currently profiling. */
PMU_OFF /* Currently stopped, but can be restarted. */
};
enum cpu_vendor_signature
{
CPU_VENDOR_UKNOWN = 0,
CPU_VENDOR_INTEL = 0x756e6547, /* Genu */
CPU_VENDOR_AMD = 0x68747541 /* Auth */
};
/* Info about pmu tool during the run time. */
struct pmu_tool_info
{
/* Current pmu tool. */
enum pmu_tool_type tool;
/* Current event. */
enum pmu_event_type event;
/* filename for storing the pmu profile. */
char *pmu_profile_filename;
/* Intermediate file where the tool stores the PMU data. */
char *raw_pmu_profile_filename;
/* Where PMU tool's stderr should be stored. */
char *tool_stderr_filename;
enum pmu_state pmu_profiling_state;
enum cpu_vendor_signature cpu_vendor; /* as discovered by cpuid */
pid_t pmu_tool_pid; /* process id of the pmu tool */
pid_t symbolizer_pid; /* process id of the symbolizer */
int symbolizer_to_pipefd[2]; /* pipe for writing to the symbolizer */
int symbolizer_from_pipefd[2]; /* pipe for reading from the symbolizer */
void *pmu_data; /* an opaque pointer for the tool to store pmu data */
int verbose; /* turn on additional debugging */
unsigned top_n_address; /* how many addresses to symbolize */
pmu_tool_fns *tool_details; /* list of functions how to start/stop/parse */
};
/* Global struct for recordkeeping. */
static struct pmu_tool_info *the_pmu_tool_info;
/* Additional info is printed if these are non-zero. */
static int tool_debug = 0;
static int sym_debug = 0;
static int parse_load_latency_line (char *line, gcov_pmu_ll_info_t *ll_info);
static int parse_branch_mispredict_line (char *line,
gcov_pmu_brm_info_t *brm_info);
static unsigned convert_pct_to_unsigned (float pct);
static void start_pfmon_module (pid_t ppid, char *tmpfile, const char **pfmon_args);
static void *init_pmu_load_latency (void);
static void *init_pmu_branch_mispredict (void);
static void destroy_load_latency_infos (void *info);
static void destroy_branch_mispredict_infos (void *info);
static int parse_pfmon_load_latency (char *filename, void *pmu_data);
static int parse_pfmon_branch_mispredicts (char *filename, void *pmu_data);
static gcov_unsigned_t gcov_tag_pmu_tool_header_length (gcov_pmu_tool_header_t
*header);
static void gcov_write_tool_header (gcov_pmu_tool_header_t *header);
static void gcov_write_load_latency_infos (void *info);
static void gcov_write_branch_mispredict_infos (void *info);
static void gcov_write_ll_line (const gcov_pmu_ll_info_t *ll_info);
static void gcov_write_branch_mispredict_line (const gcov_pmu_brm_info_t
*brm_info);
static int start_addr2line_symbolizer (pid_t pid);
static void end_addr2line_symbolizer (void);
static char *symbolize_addr2line (void *p);
static void reset_symbolizer_parent_pipes (void);
static void reset_symbolizer_child_pipes (void);
/* parse and cache relevant tool info. */
static int parse_pmu_profile_options (const char *options);
static gcov_pmu_tool_header_t *parse_pfmon_tool_header (FILE *fp,
const char *end_header);
/* How to access the necessary functions for the PMU tools. */
pmu_tool_fns all_pmu_tool_fns[PTT_LAST][PET_LAST] = {
{
{
"intel-load-latency", /* name */
pfmon_intel_ll_args, /* tool args */
init_pmu_load_latency, /* initialization */
start_pfmon_module, /* start */
0, /* stop */
parse_pfmon_load_latency, /* parse */
gcov_write_load_latency_infos, /* write */
destroy_load_latency_infos, /* cleanup */
start_addr2line_symbolizer, /* start symbolizer */
end_addr2line_symbolizer, /* end symbolizer */
symbolize_addr2line, /* symbolize */
},
{
"amd-load-latency", /* name */
pfmon_amd_ll_args, /* tool args */
init_pmu_load_latency, /* initialization */
start_pfmon_module, /* start */
0, /* stop */
parse_pfmon_load_latency, /* parse */
gcov_write_load_latency_infos, /* write */
destroy_load_latency_infos, /* cleanup */
start_addr2line_symbolizer, /* start symbolizer */
end_addr2line_symbolizer, /* end symbolizer */
symbolize_addr2line, /* symbolize */
},
{
"intel-branch-mispredict", /* name */
pfmon_intel_brm_args, /* tool args */
init_pmu_branch_mispredict, /* initialization */
start_pfmon_module, /* start */
0, /* stop */
parse_pfmon_branch_mispredicts, /* parse */
gcov_write_branch_mispredict_infos,/* write */
destroy_branch_mispredict_infos, /* cleanup */
start_addr2line_symbolizer, /* start symbolizer */
end_addr2line_symbolizer, /* end symbolizer */
symbolize_addr2line, /* symbolize */
},
{
"amd-branch-mispredict", /* name */
pfmon_amd_brm_args, /* tool args */
init_pmu_branch_mispredict, /* initialization */
start_pfmon_module, /* start */
0, /* stop */
parse_pfmon_branch_mispredicts, /* parse */
gcov_write_branch_mispredict_infos,/* write */
destroy_branch_mispredict_infos, /* cleanup */
start_addr2line_symbolizer, /* start symbolizer */
end_addr2line_symbolizer, /* end symbolizer */
symbolize_addr2line, /* symbolize */
}
}
};
/* Determine the CPU vendor. Currently only distinguishes x86 based
cpus where the vendor is either Intel or AMD. Returns one of the
enum cpu_vendor_signatures. */
static unsigned int
get_x86cpu_vendor (void)
{
unsigned int vendor = CPU_VENDOR_UKNOWN;
#if (defined (__x86_64__) || defined (__i386__))
if (__get_cpuid_max (0, &vendor) < 1)
return CPU_VENDOR_UKNOWN; /* Cannot determine cpu type. */
#endif
if (vendor == CPU_VENDOR_INTEL || vendor == CPU_VENDOR_AMD)
return vendor;
else
return CPU_VENDOR_UKNOWN;
}
/* Parse PMU tool option string provided on the command line and store
information in global structure. Return 0 on success, otherwise
return 1. Any changes to this should be synced with
check_pmu_profile_options() which does compile time check. */
static int
parse_pmu_profile_options (const char *options)
{
enum pmu_tool_type ptt = the_pmu_tool_info->tool;
enum pmu_event_type pet = PET_LAST;
const char *pmutool_path;
the_pmu_tool_info->cpu_vendor = get_x86cpu_vendor ();
/* Determine the platform we are running on. */
if (the_pmu_tool_info->cpu_vendor == CPU_VENDOR_UKNOWN)
{
/* Cpuid failed or uknown vendor. */
the_pmu_tool_info->pmu_profiling_state = PMU_ERROR;
return 1;
}
/* Validate the options. */
if (strcmp(options, "load-latency") &&
strcmp(options, "load-latency-verbose") &&
strcmp(options, "branch-mispredict") &&
strcmp(options, "branch-mispredict-verbose"))
return 1;
/* Check if are aksed to collect load latency PMU data. */
if (!strcmp(options, "load-latency") ||
!strcmp(options, "load-latency-verbose"))
{
if (the_pmu_tool_info->cpu_vendor == CPU_VENDOR_INTEL)
pet = PET_INTEL_LOAD_LATENCY;
else
pet = PET_AMD_LOAD_LATENCY;
if (!strcmp(options, "load-latency-verbose"))
the_pmu_tool_info->verbose = 1;
}
/* Check if are aksed to collect branch mispredict PMU data. */
if (!strcmp(options, "branch-mispredict") ||
!strcmp(options, "branch-mispredict-verbose"))
{
if (the_pmu_tool_info->cpu_vendor == CPU_VENDOR_INTEL)
pet = PET_INTEL_BRANCH_MISPREDICT;
else
pet = PET_AMD_BRANCH_MISPREDICT;
if (!strcmp(options, "branch-mispredict-verbose"))
the_pmu_tool_info->verbose = 1;
}
the_pmu_tool_info->tool_details = &all_pmu_tool_fns[ptt][pet];
the_pmu_tool_info->event = pet;
/* Allow users to override the default tool path. */
pmutool_path = getenv ("GCOV_PMUTOOL_PATH");
if (pmutool_path && strlen (pmutool_path))
the_pmu_tool_info->tool_details->arg_array[0] = pmutool_path;
return 0;
}
/* Do the initialization of addr2line symbolizer for the process id
given by TASK_PID. It forks an addr2line process and creates two
pipes where addresses can be written and source_filename:line_num
entries can be read. Returns 0 on success, non-zero otherwise. */
static int
start_addr2line_symbolizer (pid_t task_pid)
{
pid_t pid;
char *addr2line_path;
/* Allow users to override the default addr2line path. */
addr2line_path = getenv ("GCOV_ADDR2LINE_PATH");
if (addr2line_path && strlen (addr2line_path))
addr2line_args[0] = addr2line_path;
if (pipe (the_pmu_tool_info->symbolizer_from_pipefd) == -1)
{
fprintf (stderr, "Cannot create symbolizer write pipe.\n");
return 1;
}
if (pipe (the_pmu_tool_info->symbolizer_to_pipefd) == -1)
{
fprintf (stderr, "Cannot create symbolizer read pipe.\n");
return 1;
}
pid = fork ();
if (pid == -1)
{
/* error condition */
fprintf (stderr, "Cannot create symbolizer process.\n");
reset_symbolizer_parent_pipes ();
reset_symbolizer_child_pipes ();
return 1;
}
if (pid == 0)
{
/* child does an exec and then connects to/from the pipe */
unsigned n_args = 0;
char proc_exe_buf[128];
int new_write_fd, new_read_fd;
int i;
/* Go over the current addr2line args. */
for (i = 0; i < PMU_TOOL_MAX_ARGS && addr2line_args[i]; ++i)
n_args++;
/* We are going to add one more arg for the /proc/pid/exe */
if (n_args >= (PMU_TOOL_MAX_ARGS - 1))
{
fprintf (stderr, "too many addr2line args: %d\n", n_args);
_exit (0);
}
snprintf (proc_exe_buf, sizeof (proc_exe_buf), "/proc/%d/exe",
task_pid);
/* Add the extra arg for the process id. */
addr2line_args[n_args] = proc_exe_buf;
n_args++;
addr2line_args[n_args] = (const char *)NULL; /* terminating NULL */
if (sym_debug)
{
fprintf (stderr, "addr2line args:");
for (i = 0; i < PMU_TOOL_MAX_ARGS && addr2line_args[i]; ++i)
fprintf (stderr, " %s", addr2line_args[i]);
fprintf (stderr, "\n");
}
/* Close unused ends of the two pipes. */
reset_symbolizer_child_pipes ();
/* Connect the pipes to stdin/stdout of the child process. */
new_read_fd = dup2 (the_pmu_tool_info->symbolizer_to_pipefd[0], 0);
new_write_fd = dup2 (the_pmu_tool_info->symbolizer_from_pipefd[1], 1);
if (new_read_fd == -1 || new_write_fd == -1)
{
fprintf (stderr, "could not dup symbolizer fds\n");
reset_symbolizer_parent_pipes ();
reset_symbolizer_child_pipes ();
_exit (0);
}
the_pmu_tool_info->symbolizer_to_pipefd[0] = new_read_fd;
the_pmu_tool_info->symbolizer_from_pipefd[1] = new_write_fd;
/* Do execve with NULL env. */
execve (addr2line_args[0], (char * const*)addr2line_args,
(char * const*)NULL);
/* exec returned, an error condition. */
fprintf (stderr, "could not create symbolizer process: %s\n",
addr2line_args[0]);
reset_symbolizer_parent_pipes ();
reset_symbolizer_child_pipes ();
_exit (0);
}
else
{
/* parent */
the_pmu_tool_info->symbolizer_pid = pid;
/* Close unused ends of the two pipes. */
reset_symbolizer_parent_pipes ();
return 0;
}
return 0;
}
/* Close unused write end of the from-pipe and read end of the
to-pipe. */
static void
reset_symbolizer_parent_pipes (void)
{
if (the_pmu_tool_info->symbolizer_from_pipefd[1] != -1)
{
close (the_pmu_tool_info->symbolizer_from_pipefd[1]);
the_pmu_tool_info->symbolizer_from_pipefd[1] = -1;
}
if (the_pmu_tool_info->symbolizer_to_pipefd[0] != -1)
{
close (the_pmu_tool_info->symbolizer_to_pipefd[0]);
the_pmu_tool_info->symbolizer_to_pipefd[0] = -1;
}
}
/* Close unused write end of the to-pipe and read end of the
from-pipe. */
static void
reset_symbolizer_child_pipes (void)
{
if (the_pmu_tool_info->symbolizer_to_pipefd[1] != -1)
{
close (the_pmu_tool_info->symbolizer_to_pipefd[1]);
the_pmu_tool_info->symbolizer_to_pipefd[1] = -1;
}
if (the_pmu_tool_info->symbolizer_from_pipefd[0] != -1)
{
close (the_pmu_tool_info->symbolizer_from_pipefd[0]);
the_pmu_tool_info->symbolizer_from_pipefd[0] = -1;
}
}
/* Perform cleanup for the symbolizer process. */
static void
end_addr2line_symbolizer (void)
{
int pid_status;
int wait_status;
pid_t pid = the_pmu_tool_info->symbolizer_pid;
/* Symbolizer was not running. */
if (!pid)
return;
reset_symbolizer_parent_pipes ();
reset_symbolizer_child_pipes ();
kill (pid, SIGTERM);
wait_status = waitpid (pid, &pid_status, 0);
if (sym_debug)
{
if (wait_status == pid)
fprintf (stderr, "Normal exit. symbolizer terminated.\n");
else
fprintf (stderr, "Abnormal exit. symbolizer status, %d.\n", pid_status);
}
the_pmu_tool_info->symbolizer_pid = 0; /* Symoblizer no longer running. */
}
/* Given an address ADDR, return a string containing
source_filename:line_num entries. */
static char *
symbolize_addr2line (void *addr)
{
char buf[32]; /* holds the ascii version of address */
int write_count;
int read_count;
char *srcfile_linenum;
size_t max_length = 1024;
if (!the_pmu_tool_info->symbolizer_pid)
return default_addr2line; /* symbolizer is not running */
write_count = snprintf (buf, sizeof (buf), "%p\n", addr);
/* Write the address into the pipe. */
if (write (the_pmu_tool_info->symbolizer_to_pipefd[1], buf, write_count)
< write_count)
{
if (sym_debug)
fprintf (stderr, "Cannot write symbolizer pipe.\n");
return default_addr2line;
}
srcfile_linenum = XNEWVEC (char, max_length);
read_count = read (the_pmu_tool_info->symbolizer_from_pipefd[0],
srcfile_linenum, max_length);
if (read_count == -1)
{
if (sym_debug)
fprintf (stderr, "Cannot read symbolizer pipe.\n");
XDELETEVEC (srcfile_linenum);
return default_addr2line;
}
srcfile_linenum[read_count] = 0;
if (sym_debug)
fprintf (stderr, "symbolizer: for address %p, read_count %d, got %s\n",
addr, read_count, srcfile_linenum);
return srcfile_linenum;
}
/* Start monitoring PPID process via pfmon tool using TMPFILE as a
file to store the raw data and using PFMON_ARGS as the command line
arguments. */
static void
start_pfmon_module (pid_t ppid, char *tmpfile, const char **pfmon_args)
{
int i;
unsigned int n_args = 0;
unsigned n_chars;
char pid_buf[64];
char filename_buf[1024];
char top_n_buf[24];
unsigned extra_args;
/* Go over the current pfmon args */
for (i = 0; i < PMU_TOOL_MAX_ARGS && pfmon_args[i]; ++i)
n_args++;
if (the_pmu_tool_info->verbose)
extra_args = 4; /* account for additional --verbose */
else
extra_args = 3;
/* We are going to add args. */
if (n_args >= (PMU_TOOL_MAX_ARGS - extra_args))
{
fprintf (stderr, "too many pfmon args: %d\n", n_args);
_exit (0);
}
n_chars = snprintf (pid_buf, sizeof (pid_buf), "--attach-task=%ld",
(long)ppid);
if (n_chars >= sizeof (pid_buf))
{
fprintf (stderr, "pfmon task id too long: %s\n", pid_buf);
return;
}
pfmon_args[n_args] = pid_buf;
n_args++;
n_chars = snprintf (filename_buf, sizeof (filename_buf), "--smpl-outfile=%s",
tmpfile);
if (n_chars >= sizeof (filename_buf))
{
fprintf (stderr, "pfmon filename too long: %s\n", filename_buf);
return;
}
pfmon_args[n_args] = filename_buf;
n_args++;
n_chars = snprintf (top_n_buf, sizeof (top_n_buf), "--smpl-show-top=%d",
the_pmu_tool_info->top_n_address);
if (n_chars >= sizeof (top_n_buf))
{
fprintf (stderr, "pfmon option too long: %s\n", top_n_buf);
return;
}
pfmon_args[n_args] = top_n_buf;
n_args++;
if (the_pmu_tool_info->verbose) {
/* Add --verbose as well. */
pfmon_args[n_args] = "--verbose";
n_args++;
}
pfmon_args[n_args] = (char *)NULL;
if (tool_debug)
{
fprintf (stderr, "pfmon args:");
for (i = 0; i < PMU_TOOL_MAX_ARGS && pfmon_args[i]; ++i)
fprintf (stderr, " %s", pfmon_args[i]);
fprintf (stderr, "\n");
}
/* Do execve with NULL env. */
execve (pfmon_args[0], (char *const *)pfmon_args, (char * const*)NULL);
/* does not return */
}
/* Convert a fractional PCT to an unsigned integer after
muliplying by 100. */
static unsigned
convert_pct_to_unsigned (float pct)
{
return (unsigned)(pct * 100.0f);
}
/* Parse the load latency info pointed by LINE and save it into
LL_INFO. Returns 0 if the line was parsed successfully, non-zero
otherwise.
An example header+line look like these:
"counts %self %cum <10 <32 <64 <256 <1024 >=1024
%wself code addr symbol"
"218 24.06% 24.06% 100.00% 0.00% 0.00% 0.00% 0.00% 0.00% 22.70%
0x0000000000413e75 CalcSSIM(...)+965</tmp/psnr>"
*/
static int
parse_load_latency_line (char *line, gcov_pmu_ll_info_t *ll_info)
{
unsigned counts;
/* These are percentages parsed as floats, but then converted to
integers after multiplying by 100. */
float self, cum, lt_10, lt_32, lt_64, lt_256, lt_1024, gt_1024, wself;
long unsigned int p;
int n_values;
pmu_tool_fns *tool_details = the_pmu_tool_info->tool_details;
n_values = sscanf (line, "%u%f%%%f%%%f%%%f%%%f%%%f%%%f%%%f%%%f%%%lx",
&counts, &self, &cum, &lt_10, &lt_32, &lt_64, &lt_256,
&lt_1024, &gt_1024, &wself, &p);
if (n_values != 11)
return 1;
/* Values read successfully. Do the assignment after converting
* percentages into ints. */
ll_info->counts = counts;
ll_info->self = convert_pct_to_unsigned (self);
ll_info->cum = convert_pct_to_unsigned (cum);
ll_info->lt_10 = convert_pct_to_unsigned (lt_10);
ll_info->lt_32 = convert_pct_to_unsigned (lt_32);
ll_info->lt_64 = convert_pct_to_unsigned (lt_64);
ll_info->lt_256 = convert_pct_to_unsigned (lt_256);
ll_info->lt_1024 = convert_pct_to_unsigned (lt_1024);
ll_info->gt_1024 = convert_pct_to_unsigned (gt_1024);
ll_info->wself = convert_pct_to_unsigned (wself);
ll_info->code_addr = p;
/* Run the raw address through the symbolizer. */
if (tool_details->symbolize)
{
char *sym_info = tool_details->symbolize ((void *)p);
/* sym_info is of the form src_filename:linenum. Descriminator is
currently not supported by addr2line. */
char *sep = strchr (sym_info, ':');
if (!sep)
{
/* Assume entire string is srcfile. */
ll_info->filename = (char *)sym_info;
ll_info->line = 0;
}
else
{
/* Terminate the filename string at the separator. */
*sep = 0;
ll_info->filename = (char *)sym_info;
/* Convert rest of the sym info to a line number. */
ll_info->line = atol (sep+1);
}
ll_info->discriminator = 0;
}
else
{
/* No symbolizer available. */
ll_info->filename = NULL;
ll_info->line = 0;
ll_info->discriminator = 0;
}
return 0;
}
/* Parse the branch mispredict info pointed by LINE and save it into
BRM_INFO. Returns 0 if the line was parsed successfully, non-zero
otherwise.
An example header+line look like these:
"counts %self %cum code addr symbol"
"6869 37.67% 37.67% 0x00000000004007e5 sum(std::vector<int*,
std::allocator<int*> > const&)+51</root/tmp/array>"
*/
static int
parse_branch_mispredict_line (char *line, gcov_pmu_brm_info_t *brm_info)
{
unsigned counts;
/* These are percentages parsed as floats, but then converted to
ints after multiplying by 100. */
float self, cum;
long unsigned int p;
int n_values;
pmu_tool_fns *tool_details = the_pmu_tool_info->tool_details;
n_values = sscanf (line, "%u%f%%%f%%%lx",
&counts, &self, &cum, &p);
if (n_values != 4)
return 1;
/* Values read successfully. Do the assignment after converting
* percentages into ints. */
brm_info->counts = counts;
brm_info->self = convert_pct_to_unsigned (self);
brm_info->cum = convert_pct_to_unsigned (cum);
brm_info->code_addr = p;
/* Run the raw address through the symbolizer. */
if (tool_details->symbolize)
{
char *sym_info = tool_details->symbolize ((void *)p);
/* sym_info is of the form src_filename:linenum. Descriminator is
currently not supported by addr2line. */
char *sep = strchr (sym_info, ':');
if (!sep)
{
/* Assume entire string is srcfile. */
brm_info->filename = sym_info;
brm_info->line = 0;
}
else
{
/* Terminate the filename string at the separator. */
*sep = 0;
brm_info->filename = sym_info;
/* Convert rest of the sym info to a line number. */
brm_info->line = atol (sep+1);
}
brm_info->discriminator = 0;
}
else
{
/* No symbolizer available. */
brm_info->filename = NULL;
brm_info->line = 0;
brm_info->discriminator = 0;
}
return 0;
}
/* Delete load latency info structures INFO. */
static void
destroy_load_latency_infos (void *info)
{
unsigned i;
ll_infos_t* ll_infos = (ll_infos_t *)info;
/* delete each element */
for (i = 0; i < ll_infos->ll_count; ++i)
XDELETE (ll_infos->ll_array[i]);
/* delete the array itself */
XDELETE (ll_infos->ll_array);
__destroy_pmu_tool_header (ll_infos->pmu_tool_header);
free (ll_infos->pmu_tool_header);
ll_infos->ll_array = 0;
ll_infos->ll_count = 0;
}
/* Delete branch mispredict structure INFO. */
static void
destroy_branch_mispredict_infos (void *info)
{
unsigned i;
brm_infos_t* brm_infos = (brm_infos_t *)info;
/* delete each element */
for (i = 0; i < brm_infos->brm_count; ++i)
XDELETE (brm_infos->brm_array[i]);
/* delete the array itself */
XDELETE (brm_infos->brm_array);
__destroy_pmu_tool_header (brm_infos->pmu_tool_header);
free (brm_infos->pmu_tool_header);
brm_infos->brm_array = 0;
brm_infos->brm_count = 0;
}
/* Parse FILENAME for load latency lines into a structure
PMU_DATA. Returns 0 on on success. Returns non-zero on
failure. */
static int
parse_pfmon_load_latency (char *filename, void *pmu_data)
{
FILE *fp;
size_t buflen = 2*1024;
char *buf;
ll_infos_t *load_latency_infos = (ll_infos_t *)pmu_data;
gcov_pmu_tool_header_t *tool_header = 0;
if ((fp = fopen (filename, "r")) == NULL)
{
fprintf (stderr, "cannot open pmu data file: %s\n", filename);
return 1;
}
if (!(tool_header = parse_pfmon_tool_header (fp, pfmon_ll_header)))
{
fprintf (stderr, "cannot parse pmu data file header: %s\n", filename);
return 1;
}
buf = XNEWVEC (char, buflen);
while (fgets (buf, buflen, fp))
{
gcov_pmu_ll_info_t *ll_info = XNEW (gcov_pmu_ll_info_t);
if (!parse_load_latency_line (buf, ll_info))
{
/* valid line, add to the array */
load_latency_infos->ll_count++;
if (load_latency_infos->ll_count >=
load_latency_infos->alloc_ll_count)
{
/* need to realloc */
load_latency_infos->ll_array =
realloc (load_latency_infos->ll_array,
2 * load_latency_infos->alloc_ll_count);
if (load_latency_infos->ll_array == NULL)
{
fprintf (stderr, "Cannot allocate load latency memory.\n");
__destroy_pmu_tool_header (tool_header);
free (buf);
fclose (fp);
return 1;
}
}
load_latency_infos->ll_array[load_latency_infos->ll_count - 1] =
ll_info;
}
else
/* Delete invalid line. */
XDELETE (ll_info);
}
free (buf);
fclose (fp);
load_latency_infos->pmu_tool_header = tool_header;
return 0;
}
/* Parse open file FP until END_HEADER is seen. The data matching
gcov_pmu_tool_header_t fields is saved and returned in a new
struct. In case of failure, it returns NULL. */
static gcov_pmu_tool_header_t *
parse_pfmon_tool_header (FILE *fp, const char *end_header)
{
static const char tag_hostname[] = "# hostname: ";
static const char tag_kversion[] = "# kernel version: ";
static const char tag_hostcpu[] = "# host CPUs: ";
static const char tag_column_desc_start[] = "# description of columns:";
static const char tag_column_desc_end[] =
"# other columns are self-explanatory";
size_t buflen = 4*1024;
char *buf, *buf_start, *buf_end;
gcov_pmu_tool_header_t *tool_header = XNEWVEC (gcov_pmu_tool_header_t, 1);
char *hostname = 0;
char *kversion = 0;
char *hostcpu = 0;
char *column_description = 0;
char *column_desc_start = 0;
char *column_desc_end = 0;
const char *column_header = 0;
int got_hostname = 0;
int got_kversion = 0 ;
int got_hostcpu = 0;
int got_end_header = 0;
int got_column_description = 0;
buf = XNEWVEC (char, buflen);
buf_start = buf;
buf_end = buf + buflen;
while (buf < (buf_end - 1) && fgets (buf, buf_end - buf, fp))
{
if (strncmp (end_header, buf, buf_end - buf) == 0)
{
got_end_header = 1;
break;
}
if (!got_hostname &&
strncmp (buf, tag_hostname, strlen (tag_hostname)) == 0)
{
size_t len = strlen (buf) - strlen (tag_hostname);
hostname = XNEWVEC (char, len);
memcpy (hostname, buf + strlen (tag_hostname), len);
hostname[len - 1] = 0;
tool_header->hostname = hostname;
got_hostname = 1;
}
if (!got_kversion &&
strncmp (buf, tag_kversion, strlen (tag_kversion)) == 0)
{
size_t len = strlen (buf) - strlen (tag_kversion);
kversion = XNEWVEC (char, len);
memcpy (kversion, buf + strlen (tag_kversion), len);
kversion[len - 1] = 0;
tool_header->kernel_version = kversion;
got_kversion = 1;
}
if (!got_hostcpu &&
strncmp (buf, tag_hostcpu, strlen (tag_hostcpu)) == 0)
{
size_t len = strlen (buf) - strlen (tag_hostcpu);
hostcpu = XNEWVEC (char, len);
memcpy (hostcpu, buf + strlen (tag_hostcpu), len);
hostcpu[len - 1] = 0;
tool_header->host_cpu = hostcpu;
got_hostcpu = 1;
}
if (!got_column_description &&
strncmp (buf, tag_column_desc_start, strlen (tag_column_desc_start))
== 0)
{
column_desc_start = buf;
column_desc_end = 0;
/* Continue reading until end of the column descriptor. */
while (buf < (buf_end - 1) && fgets (buf, buf_end - buf, fp))
{
if (strncmp (buf, tag_column_desc_end,
strlen (tag_column_desc_end)) == 0)
{
column_desc_end = buf + strlen (tag_column_desc_end);
break;
}
buf += strlen (buf);
}
if (column_desc_end)
{
/* Found the end, copy it into a new string. */
column_description = XNEWVEC (char, column_desc_end -
column_desc_start + 1);
got_column_description = 1;
strcpy (column_description, column_desc_start);
tool_header->column_description = column_description;
}
}
buf += strlen (buf);
}
/* If we are missing any of the fields, return NULL. */
if (!got_end_header || !got_hostname || !got_kversion || !got_hostcpu
|| !got_column_description)
{
free (hostname);
free (kversion);
free (hostcpu);
free (column_description);
free (buf_start);
free (tool_header);
return NULL;
}
switch (the_pmu_tool_info->event)
{
case PET_INTEL_LOAD_LATENCY:
case PET_AMD_LOAD_LATENCY:
column_header = pfmon_ll_header;
break;
case PET_INTEL_BRANCH_MISPREDICT:
case PET_AMD_BRANCH_MISPREDICT:
column_header = pfmon_bm_header;
break;
default:
break;
}
tool_header->column_header = strdup (column_header);
tool_header->full_header = buf_start;
return tool_header;
}
/* Parse FILENAME for branch mispredict lines into a structure
PMU_DATA. Returns 0 on on success. Returns non-zero on
failure. */
static int
parse_pfmon_branch_mispredicts (char *filename, void *pmu_data)
{
FILE *fp;
size_t buflen = 2*1024;
char *buf;
brm_infos_t *brm_infos = (brm_infos_t *)pmu_data;
gcov_pmu_tool_header_t *tool_header = 0;
if ((fp = fopen (filename, "r")) == NULL)
{
fprintf (stderr, "cannot open pmu data file: %s\n", filename);
return 1;
}
if (!(tool_header = parse_pfmon_tool_header (fp, pfmon_bm_header)))
{
fprintf (stderr, "cannot parse pmu data file header: %s\n", filename);
return 1;
}
buf = XNEWVEC (char, buflen);
while (fgets (buf, buflen, fp))
{
gcov_pmu_brm_info_t *brm = XNEW (gcov_pmu_brm_info_t);
if (!parse_branch_mispredict_line (buf, brm))
{
/* Valid line, add to the array. */
brm_infos->brm_count++;
if (brm_infos->brm_count >= brm_infos->alloc_brm_count)
{
/* Do we need to realloc? */
brm_infos->brm_array =
realloc (brm_infos->brm_array,
2 * brm_infos->alloc_brm_count);
if (brm_infos->brm_array == NULL) {
fprintf (stderr,
"Cannot allocate memory for br mispredicts.\n");
__destroy_pmu_tool_header (tool_header);
free (buf);
fclose (fp);
return 1;
}
}
brm_infos->brm_array[brm_infos->brm_count - 1] = brm;
}
else
/* Delete invalid line. */
XDELETE (brm);
}
free (buf);
fclose (fp);
brm_infos->pmu_tool_header = tool_header;
return 0;
}
/* Start the monitoring process using pmu tool. Return 0 on success,
non-zero otherwise. */
static int
pmu_start (void)
{
pid_t pid;
/* no start function */
if (!the_pmu_tool_info->tool_details->start_pmu_module)
return 1;
pid = fork ();
if (pid == -1)
{
/* error condition */
fprintf (stderr, "Cannot create PMU profiling process, exiting.\n");
return 1;
}
else if (pid == 0)
{
/* child */
pid_t ppid = getppid();
char *tmpfile = the_pmu_tool_info->raw_pmu_profile_filename;
const char **pfmon_args = the_pmu_tool_info->tool_details->arg_array;
int new_stderr_fd;
/* Redirect stderr from the child process into a separate file. */
new_stderr_fd = creat (the_pmu_tool_info->tool_stderr_filename,
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
if (new_stderr_fd != -1)
dup2 (new_stderr_fd, 2);
/* The following does an exec and thus is not expected to return. */
the_pmu_tool_info->tool_details->start_pmu_module(ppid, tmpfile,
pfmon_args);
/* exec returned, an error condition. */
fprintf (stderr, "could not create profiling process: %s\n",
the_pmu_tool_info->tool_details->arg_array[0]);
_exit (0);
}
else
{
/* parent */
the_pmu_tool_info->pmu_tool_pid = pid;
return 0;
}
}
/* Allocate and initialize pmu load latency structure. */
static void *
init_pmu_load_latency (void)
{
ll_infos_t *load_latency = XNEWVEC (ll_infos_t, 1);
load_latency->ll_count = 0;
load_latency->alloc_ll_count = 64;
load_latency->ll_array = XNEWVEC (gcov_pmu_ll_info_t *,
load_latency->alloc_ll_count);
return (void *)load_latency;
}
/* Allocate and initialize pmu branch mispredict structure. */
static void *
init_pmu_branch_mispredict (void)
{
brm_infos_t *brm_info = XNEWVEC (brm_infos_t, 1);
brm_info->brm_count = 0;
brm_info->alloc_brm_count = 64;
brm_info->brm_array = XNEWVEC (gcov_pmu_brm_info_t *,
brm_info->alloc_brm_count);
return (void *)brm_info;
}
/* Initialize pmu tool based upon PMU_INFO. Sets the appropriate tool
type in the global the_pmu_tool_info. */
static int
init_pmu_tool (struct gcov_pmu_info *pmu_info)
{
the_pmu_tool_info->pmu_profiling_state = PMU_NONE;
the_pmu_tool_info->verbose = 0;
the_pmu_tool_info->tool = PTT_PFMON; /* we support only pfmon */
the_pmu_tool_info->pmu_tool_pid = 0;
the_pmu_tool_info->top_n_address = pmu_info->pmu_top_n_address;
the_pmu_tool_info->symbolizer_pid = 0;
the_pmu_tool_info->symbolizer_to_pipefd[0] = -1;
the_pmu_tool_info->symbolizer_to_pipefd[1] = -1;
the_pmu_tool_info->symbolizer_from_pipefd[0] = -1;
the_pmu_tool_info->symbolizer_from_pipefd[1] = -1;
if (parse_pmu_profile_options (pmu_info->pmu_tool))
return 1;
if (the_pmu_tool_info->pmu_profiling_state == PMU_ERROR)
{
fprintf (stderr, "Unsupported PMU module: %s, disabling PMU profiling.\n",
pmu_info->pmu_tool);
return 1;
}
if (the_pmu_tool_info->tool_details->init_pmu_module)
/* initialize module */
the_pmu_tool_info->pmu_data =
the_pmu_tool_info->tool_details->init_pmu_module();
return 0;
}
/* Initialize PMU profiling based upon the information passed in
PMU_INFO and use pmu_profile_filename as the file to store the PMU
profile. This is called multiple times from libgcov, once per
object file. We need to make sure to do the necessary
initialization only the first time. For subsequent invocations it
behaves as a NOOP. */
void
__gcov_init_pmu_profiler (struct gcov_pmu_info *pmu_info)
{
char *raw_pmu_profile_filename;
char *tool_stderr_filename;
if (!pmu_info || !pmu_info->pmu_profile_filename || !pmu_info->pmu_tool)
return;
/* Allocate the global structure on first invocation. */
if (!the_pmu_tool_info)
{
the_pmu_tool_info = XNEWVEC (struct pmu_tool_info, 1);
if (!the_pmu_tool_info)
{
fprintf (stderr, "Error allocating memory for PMU tool\n");
return;
}
if (init_pmu_tool (pmu_info))
{
/* Initialization error. */
XDELETE (the_pmu_tool_info);
the_pmu_tool_info = 0;
return;
}
}
switch (the_pmu_tool_info->pmu_profiling_state)
{
case PMU_NONE:
the_pmu_tool_info->pmu_profile_filename =
strdup (pmu_info->pmu_profile_filename);
/* Construct an intermediate filename by substituting trailing
'.gcda' with '.pmud'. */
raw_pmu_profile_filename = strdup (pmu_info->pmu_profile_filename);
if (raw_pmu_profile_filename == NULL)
{
fprintf (stderr, "Cannot allocate memory\n");
exit (1);
}
strcpy (raw_pmu_profile_filename + strlen (raw_pmu_profile_filename) - 4,
"pmud");
/* Construct a filename for collecting PMU tool's stderr by
substituting trailing '.gcda' with '.stderr'. */
tool_stderr_filename =
XNEWVEC (char, strlen (pmu_info->pmu_profile_filename) + 1 + 2);
strcpy (tool_stderr_filename, pmu_info->pmu_profile_filename);
strcpy (tool_stderr_filename + strlen (tool_stderr_filename) - 4,
"stderr");
the_pmu_tool_info->raw_pmu_profile_filename = raw_pmu_profile_filename;
the_pmu_tool_info->tool_stderr_filename = tool_stderr_filename;
the_pmu_tool_info->pmu_profiling_state = PMU_INITIALIZED;
break;
case PMU_INITIALIZED:
case PMU_OFF:
case PMU_ON:
case PMU_ERROR:
break;
default:
break;
}
}
/* Start PMU profiling. It updates the current state. */
void
__gcov_start_pmu_profiler (void)
{
if (!the_pmu_tool_info)
return;
switch (the_pmu_tool_info->pmu_profiling_state)
{
case PMU_INITIALIZED:
if (!pmu_start ())
the_pmu_tool_info->pmu_profiling_state = PMU_ON;
else
the_pmu_tool_info->pmu_profiling_state = PMU_ERROR;
break;
case PMU_NONE:
/* PMU was not properly initialized, don't attempt start it. */
the_pmu_tool_info->pmu_profiling_state = PMU_ERROR;
break;
case PMU_OFF:
/* Restarting PMU is not yet supported. */
case PMU_ON:
/* Do nothing. */
case PMU_ERROR:
break;
default:
break;
}
}
/* Stop PMU profiling. Currently it doesn't do anything except
bookkeeping. */
void
__gcov_stop_pmu_profiler (void)
{
if (!the_pmu_tool_info)
return;
if (the_pmu_tool_info->tool_details->stop_pmu_module)
the_pmu_tool_info->tool_details->stop_pmu_module();
if (the_pmu_tool_info->pmu_profiling_state == PMU_ON)
the_pmu_tool_info->pmu_profiling_state = PMU_OFF;
}
/* Write the load latency information LL_INFO into the gcda file. */
static void
gcov_write_ll_line (const gcov_pmu_ll_info_t *ll_info)
{
gcov_unsigned_t len = GCOV_TAG_PMU_LOAD_LATENCY_LENGTH (ll_info->filename);
gcov_write_tag_length (GCOV_TAG_PMU_LOAD_LATENCY_INFO, len);
gcov_write_unsigned (ll_info->counts);
gcov_write_unsigned (ll_info->self);
gcov_write_unsigned (ll_info->cum);
gcov_write_unsigned (ll_info->lt_10);
gcov_write_unsigned (ll_info->lt_32);
gcov_write_unsigned (ll_info->lt_64);
gcov_write_unsigned (ll_info->lt_256);
gcov_write_unsigned (ll_info->lt_1024);
gcov_write_unsigned (ll_info->gt_1024);
gcov_write_unsigned (ll_info->wself);
gcov_write_counter (ll_info->code_addr);
gcov_write_unsigned (ll_info->line);
gcov_write_unsigned (ll_info->discriminator);
gcov_write_string (ll_info->filename);
}
/* Write the branch mispredict information BRM_INFO into the gcda file. */
static void
gcov_write_branch_mispredict_line (const gcov_pmu_brm_info_t *brm_info)
{
gcov_unsigned_t len = GCOV_TAG_PMU_BRANCH_MISPREDICT_LENGTH (
brm_info->filename);
gcov_write_tag_length (GCOV_TAG_PMU_BRANCH_MISPREDICT_INFO, len);
gcov_write_unsigned (brm_info->counts);
gcov_write_unsigned (brm_info->self);
gcov_write_unsigned (brm_info->cum);
gcov_write_counter (brm_info->code_addr);
gcov_write_unsigned (brm_info->line);
gcov_write_unsigned (brm_info->discriminator);
gcov_write_string (brm_info->filename);
}
/* Write load latency information INFO into the gcda file. The gcda
file has already been opened and is available for writing. */
static void
gcov_write_load_latency_infos (void *info)
{
unsigned i;
const ll_infos_t *ll_infos = (const ll_infos_t *)info;
gcov_unsigned_t stamp = 0; /* Don't use stamp as we don't support merge. */
/* We don't support merge, and instead always rewrite the file. But
to rewrite a gcov file we must first read it, however the read
value is ignored. */
gcov_read_unsigned ();
gcov_rewrite ();
gcov_write_tag_length (GCOV_DATA_MAGIC, GCOV_VERSION);
gcov_write_unsigned (stamp);
if (ll_infos->pmu_tool_header)
gcov_write_tool_header (ll_infos->pmu_tool_header);
for (i = 0; i < ll_infos->ll_count; ++i)
{
/* Write each line. */
gcov_write_ll_line (ll_infos->ll_array[i]);
}
gcov_truncate ();
}
/* Write branch mispredict information INFO into the gcda file. The
gcda file has already been opened and is available for writing. */
static void
gcov_write_branch_mispredict_infos (void *info)
{
unsigned i;
const brm_infos_t *brm_infos = (const brm_infos_t *)info;
gcov_unsigned_t stamp = 0; /* Don't use stamp as we don't support merge. */
/* We don't support merge, and instead always rewrite the file. */
gcov_rewrite ();
gcov_write_tag_length (GCOV_DATA_MAGIC, GCOV_VERSION);
gcov_write_unsigned (stamp);
if (brm_infos->pmu_tool_header)
gcov_write_tool_header (brm_infos->pmu_tool_header);
for (i = 0; i < brm_infos->brm_count; ++i)
{
/* Write each line. */
gcov_write_branch_mispredict_line (brm_infos->brm_array[i]);
}
gcov_truncate ();
}
/* Compute TOOL_HEADER length for writing into the gcov file. */
static gcov_unsigned_t
gcov_tag_pmu_tool_header_length (gcov_pmu_tool_header_t *header)
{
gcov_unsigned_t len = 0;
if (header)
{
len += gcov_string_length (header->host_cpu);
len += gcov_string_length (header->hostname);
len += gcov_string_length (header->kernel_version);
len += gcov_string_length (header->column_header);
len += gcov_string_length (header->column_description);
len += gcov_string_length (header->full_header);
}
return len;
}
/* Write tool header into the gcda file. It assumes that the gcda file
has already been opened and is available for writing. */
static void
gcov_write_tool_header (gcov_pmu_tool_header_t *header)
{
gcov_unsigned_t len = gcov_tag_pmu_tool_header_length (header);
gcov_write_tag_length (GCOV_TAG_PMU_TOOL_HEADER, len);
gcov_write_string (header->host_cpu);
gcov_write_string (header->hostname);
gcov_write_string (header->kernel_version);
gcov_write_string (header->column_header);
gcov_write_string (header->column_description);
gcov_write_string (header->full_header);
}
/* End PMU profiling. If GCDA_ERROR is non-zero then write profiling data into
already open gcda file */
void
__gcov_end_pmu_profiler (int gcda_error)
{
int pid_status;
int wait_status;
pid_t pid;
pmu_tool_fns *tool_details;
if (!the_pmu_tool_info)
return;
tool_details = the_pmu_tool_info->tool_details;
pid = the_pmu_tool_info->pmu_tool_pid;
if (pid)
{
if (tool_debug)
fprintf (stderr, "terminating PMU profiling process %ld\n", (long)pid);
kill (pid, SIGTERM);
if (tool_debug)
fprintf (stderr, "parent: waiting for pmu process to end\n");
wait_status = waitpid (pid, &pid_status, 0);
if (tool_debug) {
if (wait_status == pid)
fprintf (stderr, "Normal exit. Child terminated.\n");
else
fprintf (stderr, "Abnormal exit. child status, %d.\n", pid_status);
}
}
if (the_pmu_tool_info->pmu_profiling_state != PMU_OFF)
{
/* nothing to do */
fprintf (stderr,
"__gcov_dump_pmu_profile: incorrect pmu state: %d, pid: %ld\n",
the_pmu_tool_info->pmu_profiling_state,
(unsigned long)pid);
return;
}
if (!tool_details->parse_pmu_output)
return;
/* Since we are going to parse the output, we also need symbolizer. */
if (tool_details->start_symbolizer)
tool_details->start_symbolizer (getpid ());
if (!tool_details->parse_pmu_output
(the_pmu_tool_info->raw_pmu_profile_filename,
the_pmu_tool_info->pmu_data))
{
if (!gcda_error && tool_details->gcov_write_pmu_data)
/* Write tool output into the gcda file. */
tool_details->gcov_write_pmu_data (the_pmu_tool_info->pmu_data);
}
if (tool_details->end_symbolizer)
tool_details->end_symbolizer ();
if (tool_details->cleanup_pmu_data)
tool_details->cleanup_pmu_data (the_pmu_tool_info->pmu_data);
}
#endif