Merge from trunk: 215733-215743
[official-gcc.git] / main / libgcc / pmu-profile.c
blob14a5132e4f2ec78c397a8215dec18c0c7467a5a0
1 /* Performance monitoring unit (PMU) profiler. If available, use an
2 external tool to collect hardware performance counter data and
3 write it in the .gcda files.
5 Copyright (C) 2011. Free Software Foundation, Inc.
6 Contributed by Sharad Singhai <singhai@google.com>.
8 This file is part of GCC.
10 GCC is free software; you can redistribute it and/or modify it under
11 the terms of the GNU General Public License as published by the Free
12 Software Foundation; either version 3, or (at your option) any later
13 version.
15 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
16 WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 for more details.
20 Under Section 7 of GPL version 3, you are granted additional
21 permissions described in the GCC Runtime Library Exception, version
22 3.1, as published by the Free Software Foundation.
24 You should have received a copy of the GNU General Public License and
25 a copy of the GCC Runtime Library Exception along with this program;
26 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
27 <http://www.gnu.org/licenses/>. */
29 #include "tconfig.h"
30 #include "tsystem.h"
31 #include "coretypes.h"
32 #include "tm.h"
33 #if (defined (__x86_64__) || defined (__i386__))
34 #include "cpuid.h"
35 #endif
37 #if defined(inhibit_libc)
38 #define IN_LIBGCOV (-1)
39 #else
40 #include <stdio.h>
41 #include <stdlib.h>
42 #define IN_LIBGCOV 1
43 #if defined(L_gcov)
44 #define GCOV_LINKAGE /* nothing */
45 #endif
46 #endif
47 #include "gcov-io.h"
48 #ifdef TARGET_POSIX_IO
49 #include <fcntl.h>
50 #include <signal.h>
51 #include <sys/stat.h>
52 #include <sys/types.h>
53 #endif
55 #if defined(inhibit_libc)
56 #else
57 #include <errno.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <unistd.h>
62 #include <sys/types.h>
63 #include <sys/wait.h>
65 #define XNEWVEC(type,ne) (type *)calloc((ne),sizeof(type))
66 #define XNEW(type) (type *)malloc(sizeof(type))
67 #define XDELETEVEC(p) free(p)
68 #define XDELETE(p) free(p)
70 #define PFMON_CMD "/usr/bin/pfmon"
71 #define ADDR2LINE_CMD "/usr/bin/addr2line"
72 #define PMU_TOOL_MAX_ARGS (20)
73 static char default_addr2line[] = "??:0";
74 static const char pfmon_ll_header[] = "# counts %self %cum "
75 "<10 <32 <64 <256 <1024 >=1024 %wself "
76 "code addr symbol\n";
77 static const char pfmon_bm_header[] =
78 "# counts %self %cum code addr symbol\n";
80 const char *pfmon_intel_ll_args[PMU_TOOL_MAX_ARGS] = {
81 PFMON_CMD,
82 "--aggregate-results",
83 "--follow-all",
84 "--with-header",
85 "--smpl-module=pebs-ll",
86 "--ld-lat-threshold=4",
87 "--pebs-ll-dcmiss-code",
88 "--resolve-addresses",
89 "-emem_inst_retired:LATENCY_ABOVE_THRESHOLD",
90 "--long-smpl-periods=10000",
91 0 /* terminating NULL must be present */
94 const char *pfmon_amd_ll_args[PMU_TOOL_MAX_ARGS] = {
95 PFMON_CMD,
96 "--aggregate-results",
97 "--follow-all",
98 "-uk",
99 "--with-header",
100 "--smpl-module=ibs",
101 "--resolve-addresses",
102 "-eibsop_event:uops",
103 "--ibs-dcmiss-code",
104 "--long-smpl-periods=0xffff0",
105 0 /* terminating NULL must be present */
108 const char *pfmon_intel_brm_args[PMU_TOOL_MAX_ARGS] = {
109 PFMON_CMD,
110 "--aggregate-results",
111 "--follow-all",
112 "--with-header",
113 "--resolve-addresses",
114 "-eMISPREDICTED_BRANCH_RETIRED",
115 "--long-smpl-periods=10000",
116 0 /* terminating NULL must be present */
119 const char *pfmon_amd_brm_args[PMU_TOOL_MAX_ARGS] = {
120 PFMON_CMD,
121 "--aggregate-results",
122 "--follow-all",
123 "--with-header",
124 "--resolve-addresses",
125 "-eRETIRED_MISPREDICTED_BRANCH_INSTRUCTIONS",
126 "--long-smpl-periods=10000",
127 0 /* terminating NULL must be present */
130 const char *addr2line_args[PMU_TOOL_MAX_ARGS] = {
131 ADDR2LINE_CMD,
132 "-e",
133 0 /* terminating NULL must be present */
137 enum pmu_tool_type
139 PTT_PFMON,
140 PTT_LAST
143 enum pmu_event_type
145 PET_INTEL_LOAD_LATENCY,
146 PET_AMD_LOAD_LATENCY,
147 PET_INTEL_BRANCH_MISPREDICT,
148 PET_AMD_BRANCH_MISPREDICT,
149 PET_LAST
152 typedef struct pmu_tool_fns {
153 const char *name; /* name of the pmu tool */
154 /* pmu tool commandline argument. */
155 const char **arg_array;
156 /* Initialize pmu module. */
157 void *(*init_pmu_module) (void);
158 /* Start profililing. */
159 void (*start_pmu_module) (pid_t ppid, char *tmpfile, const char **args);
160 /* Stop profililing. */
161 void (*stop_pmu_module) (void);
162 /* How to parse the output generated by the PMU tool. */
163 int (*parse_pmu_output) (char *filename, void *pmu_data);
164 /* How to write parsed pmu data into gcda file. */
165 void (*gcov_write_pmu_data) (void *data);
166 /* How to cleanup any data structure created during parsing. */
167 void (*cleanup_pmu_data) (void *data);
168 /* How to initialize symbolizer for the PPID. */
169 int (*start_symbolizer) (pid_t ppid);
170 void (*end_symbolizer) (void);
171 char *(*symbolize) (void *addr);
172 } pmu_tool_fns;
174 enum pmu_state
176 PMU_NONE, /* Not configurated at all. */
177 PMU_INITIALIZED, /* Configured and initialized. */
178 PMU_ERROR, /* Configuration error. Cannot recover. */
179 PMU_ON, /* Currently profiling. */
180 PMU_OFF /* Currently stopped, but can be restarted. */
183 enum cpu_vendor_signature
185 CPU_VENDOR_UKNOWN = 0,
186 CPU_VENDOR_INTEL = 0x756e6547, /* Genu */
187 CPU_VENDOR_AMD = 0x68747541 /* Auth */
190 /* Info about pmu tool during the run time. */
191 struct pmu_tool_info
193 /* Current pmu tool. */
194 enum pmu_tool_type tool;
195 /* Current event. */
196 enum pmu_event_type event;
197 /* filename for storing the pmu profile. */
198 char *pmu_profile_filename;
199 /* Intermediate file where the tool stores the PMU data. */
200 char *raw_pmu_profile_filename;
201 /* Where PMU tool's stderr should be stored. */
202 char *tool_stderr_filename;
203 enum pmu_state pmu_profiling_state;
204 enum cpu_vendor_signature cpu_vendor; /* as discovered by cpuid */
205 pid_t pmu_tool_pid; /* process id of the pmu tool */
206 pid_t symbolizer_pid; /* process id of the symbolizer */
207 int symbolizer_to_pipefd[2]; /* pipe for writing to the symbolizer */
208 int symbolizer_from_pipefd[2]; /* pipe for reading from the symbolizer */
209 void *pmu_data; /* an opaque pointer for the tool to store pmu data */
210 int verbose; /* turn on additional debugging */
211 unsigned top_n_address; /* how many addresses to symbolize */
212 pmu_tool_fns *tool_details; /* list of functions how to start/stop/parse */
215 /* Global struct for recordkeeping. */
216 static struct pmu_tool_info *the_pmu_tool_info;
218 /* Additional info is printed if these are non-zero. */
219 static int tool_debug = 0;
220 static int sym_debug = 0;
222 static int parse_load_latency_line (char *line, gcov_pmu_ll_info_t *ll_info);
223 static int parse_branch_mispredict_line (char *line,
224 gcov_pmu_brm_info_t *brm_info);
225 static unsigned convert_pct_to_unsigned (float pct);
226 static void start_pfmon_module (pid_t ppid, char *tmpfile, const char **pfmon_args);
227 static void *init_pmu_load_latency (void);
228 static void *init_pmu_branch_mispredict (void);
229 static void destroy_load_latency_infos (void *info);
230 static void destroy_branch_mispredict_infos (void *info);
231 static int parse_pfmon_load_latency (char *filename, void *pmu_data);
232 static int parse_pfmon_branch_mispredicts (char *filename, void *pmu_data);
233 static gcov_unsigned_t gcov_tag_pmu_tool_header_length (gcov_pmu_tool_header_t
234 *header);
235 static void gcov_write_tool_header (gcov_pmu_tool_header_t *header);
236 static void gcov_write_load_latency_infos (void *info);
237 static void gcov_write_branch_mispredict_infos (void *info);
238 static void gcov_write_ll_line (const gcov_pmu_ll_info_t *ll_info);
239 static void gcov_write_branch_mispredict_line (const gcov_pmu_brm_info_t
240 *brm_info);
241 static int start_addr2line_symbolizer (pid_t pid);
242 static void end_addr2line_symbolizer (void);
243 static char *symbolize_addr2line (void *p);
244 static void reset_symbolizer_parent_pipes (void);
245 static void reset_symbolizer_child_pipes (void);
246 /* parse and cache relevant tool info. */
247 static int parse_pmu_profile_options (const char *options);
248 static gcov_pmu_tool_header_t *parse_pfmon_tool_header (FILE *fp,
249 const char *end_header);
252 /* How to access the necessary functions for the PMU tools. */
253 pmu_tool_fns all_pmu_tool_fns[PTT_LAST][PET_LAST] = {
256 "intel-load-latency", /* name */
257 pfmon_intel_ll_args, /* tool args */
258 init_pmu_load_latency, /* initialization */
259 start_pfmon_module, /* start */
260 0, /* stop */
261 parse_pfmon_load_latency, /* parse */
262 gcov_write_load_latency_infos, /* write */
263 destroy_load_latency_infos, /* cleanup */
264 start_addr2line_symbolizer, /* start symbolizer */
265 end_addr2line_symbolizer, /* end symbolizer */
266 symbolize_addr2line, /* symbolize */
269 "amd-load-latency", /* name */
270 pfmon_amd_ll_args, /* tool args */
271 init_pmu_load_latency, /* initialization */
272 start_pfmon_module, /* start */
273 0, /* stop */
274 parse_pfmon_load_latency, /* parse */
275 gcov_write_load_latency_infos, /* write */
276 destroy_load_latency_infos, /* cleanup */
277 start_addr2line_symbolizer, /* start symbolizer */
278 end_addr2line_symbolizer, /* end symbolizer */
279 symbolize_addr2line, /* symbolize */
282 "intel-branch-mispredict", /* name */
283 pfmon_intel_brm_args, /* tool args */
284 init_pmu_branch_mispredict, /* initialization */
285 start_pfmon_module, /* start */
286 0, /* stop */
287 parse_pfmon_branch_mispredicts, /* parse */
288 gcov_write_branch_mispredict_infos,/* write */
289 destroy_branch_mispredict_infos, /* cleanup */
290 start_addr2line_symbolizer, /* start symbolizer */
291 end_addr2line_symbolizer, /* end symbolizer */
292 symbolize_addr2line, /* symbolize */
295 "amd-branch-mispredict", /* name */
296 pfmon_amd_brm_args, /* tool args */
297 init_pmu_branch_mispredict, /* initialization */
298 start_pfmon_module, /* start */
299 0, /* stop */
300 parse_pfmon_branch_mispredicts, /* parse */
301 gcov_write_branch_mispredict_infos,/* write */
302 destroy_branch_mispredict_infos, /* cleanup */
303 start_addr2line_symbolizer, /* start symbolizer */
304 end_addr2line_symbolizer, /* end symbolizer */
305 symbolize_addr2line, /* symbolize */
310 /* Determine the CPU vendor. Currently only distinguishes x86 based
311 cpus where the vendor is either Intel or AMD. Returns one of the
312 enum cpu_vendor_signatures. */
314 static unsigned int
315 get_x86cpu_vendor (void)
317 unsigned int vendor = CPU_VENDOR_UKNOWN;
319 #if (defined (__x86_64__) || defined (__i386__))
320 if (__get_cpuid_max (0, &vendor) < 1)
321 return CPU_VENDOR_UKNOWN; /* Cannot determine cpu type. */
322 #endif
324 if (vendor == CPU_VENDOR_INTEL || vendor == CPU_VENDOR_AMD)
325 return vendor;
326 else
327 return CPU_VENDOR_UKNOWN;
331 /* Parse PMU tool option string provided on the command line and store
332 information in global structure. Return 0 on success, otherwise
333 return 1. Any changes to this should be synced with
334 check_pmu_profile_options() which does compile time check. */
336 static int
337 parse_pmu_profile_options (const char *options)
339 enum pmu_tool_type ptt = the_pmu_tool_info->tool;
340 enum pmu_event_type pet = PET_LAST;
341 const char *pmutool_path;
342 the_pmu_tool_info->cpu_vendor = get_x86cpu_vendor ();
343 /* Determine the platform we are running on. */
344 if (the_pmu_tool_info->cpu_vendor == CPU_VENDOR_UKNOWN)
346 /* Cpuid failed or uknown vendor. */
347 the_pmu_tool_info->pmu_profiling_state = PMU_ERROR;
348 return 1;
351 /* Validate the options. */
352 if (strcmp(options, "load-latency") &&
353 strcmp(options, "load-latency-verbose") &&
354 strcmp(options, "branch-mispredict") &&
355 strcmp(options, "branch-mispredict-verbose"))
356 return 1;
358 /* Check if are aksed to collect load latency PMU data. */
359 if (!strcmp(options, "load-latency") ||
360 !strcmp(options, "load-latency-verbose"))
362 if (the_pmu_tool_info->cpu_vendor == CPU_VENDOR_INTEL)
363 pet = PET_INTEL_LOAD_LATENCY;
364 else
365 pet = PET_AMD_LOAD_LATENCY;
366 if (!strcmp(options, "load-latency-verbose"))
367 the_pmu_tool_info->verbose = 1;
370 /* Check if are aksed to collect branch mispredict PMU data. */
371 if (!strcmp(options, "branch-mispredict") ||
372 !strcmp(options, "branch-mispredict-verbose"))
374 if (the_pmu_tool_info->cpu_vendor == CPU_VENDOR_INTEL)
375 pet = PET_INTEL_BRANCH_MISPREDICT;
376 else
377 pet = PET_AMD_BRANCH_MISPREDICT;
378 if (!strcmp(options, "branch-mispredict-verbose"))
379 the_pmu_tool_info->verbose = 1;
382 the_pmu_tool_info->tool_details = &all_pmu_tool_fns[ptt][pet];
383 the_pmu_tool_info->event = pet;
385 /* Allow users to override the default tool path. */
386 pmutool_path = getenv ("GCOV_PMUTOOL_PATH");
387 if (pmutool_path && strlen (pmutool_path))
388 the_pmu_tool_info->tool_details->arg_array[0] = pmutool_path;
390 return 0;
393 /* Do the initialization of addr2line symbolizer for the process id
394 given by TASK_PID. It forks an addr2line process and creates two
395 pipes where addresses can be written and source_filename:line_num
396 entries can be read. Returns 0 on success, non-zero otherwise. */
398 static int
399 start_addr2line_symbolizer (pid_t task_pid)
401 pid_t pid;
402 char *addr2line_path;
404 /* Allow users to override the default addr2line path. */
405 addr2line_path = getenv ("GCOV_ADDR2LINE_PATH");
406 if (addr2line_path && strlen (addr2line_path))
407 addr2line_args[0] = addr2line_path;
409 if (pipe (the_pmu_tool_info->symbolizer_from_pipefd) == -1)
411 fprintf (stderr, "Cannot create symbolizer write pipe.\n");
412 return 1;
414 if (pipe (the_pmu_tool_info->symbolizer_to_pipefd) == -1)
416 fprintf (stderr, "Cannot create symbolizer read pipe.\n");
417 return 1;
420 pid = fork ();
421 if (pid == -1)
423 /* error condition */
424 fprintf (stderr, "Cannot create symbolizer process.\n");
425 reset_symbolizer_parent_pipes ();
426 reset_symbolizer_child_pipes ();
427 return 1;
430 if (pid == 0)
432 /* child does an exec and then connects to/from the pipe */
433 unsigned n_args = 0;
434 char proc_exe_buf[128];
435 int new_write_fd, new_read_fd;
436 int i;
438 /* Go over the current addr2line args. */
439 for (i = 0; i < PMU_TOOL_MAX_ARGS && addr2line_args[i]; ++i)
440 n_args++;
442 /* We are going to add one more arg for the /proc/pid/exe */
443 if (n_args >= (PMU_TOOL_MAX_ARGS - 1))
445 fprintf (stderr, "too many addr2line args: %d\n", n_args);
446 _exit (0);
448 snprintf (proc_exe_buf, sizeof (proc_exe_buf), "/proc/%d/exe",
449 task_pid);
451 /* Add the extra arg for the process id. */
452 addr2line_args[n_args] = proc_exe_buf;
453 n_args++;
455 addr2line_args[n_args] = (const char *)NULL; /* terminating NULL */
457 if (sym_debug)
459 fprintf (stderr, "addr2line args:");
460 for (i = 0; i < PMU_TOOL_MAX_ARGS && addr2line_args[i]; ++i)
461 fprintf (stderr, " %s", addr2line_args[i]);
462 fprintf (stderr, "\n");
465 /* Close unused ends of the two pipes. */
466 reset_symbolizer_child_pipes ();
468 /* Connect the pipes to stdin/stdout of the child process. */
469 new_read_fd = dup2 (the_pmu_tool_info->symbolizer_to_pipefd[0], 0);
470 new_write_fd = dup2 (the_pmu_tool_info->symbolizer_from_pipefd[1], 1);
471 if (new_read_fd == -1 || new_write_fd == -1)
473 fprintf (stderr, "could not dup symbolizer fds\n");
474 reset_symbolizer_parent_pipes ();
475 reset_symbolizer_child_pipes ();
476 _exit (0);
478 the_pmu_tool_info->symbolizer_to_pipefd[0] = new_read_fd;
479 the_pmu_tool_info->symbolizer_from_pipefd[1] = new_write_fd;
481 /* Do execve with NULL env. */
482 execve (addr2line_args[0], (char * const*)addr2line_args,
483 (char * const*)NULL);
484 /* exec returned, an error condition. */
485 fprintf (stderr, "could not create symbolizer process: %s\n",
486 addr2line_args[0]);
487 reset_symbolizer_parent_pipes ();
488 reset_symbolizer_child_pipes ();
489 _exit (0);
491 else
493 /* parent */
494 the_pmu_tool_info->symbolizer_pid = pid;
495 /* Close unused ends of the two pipes. */
496 reset_symbolizer_parent_pipes ();
497 return 0;
499 return 0;
502 /* Close unused write end of the from-pipe and read end of the
503 to-pipe. */
505 static void
506 reset_symbolizer_parent_pipes (void)
508 if (the_pmu_tool_info->symbolizer_from_pipefd[1] != -1)
510 close (the_pmu_tool_info->symbolizer_from_pipefd[1]);
511 the_pmu_tool_info->symbolizer_from_pipefd[1] = -1;
513 if (the_pmu_tool_info->symbolizer_to_pipefd[0] != -1)
515 close (the_pmu_tool_info->symbolizer_to_pipefd[0]);
516 the_pmu_tool_info->symbolizer_to_pipefd[0] = -1;
520 /* Close unused write end of the to-pipe and read end of the
521 from-pipe. */
523 static void
524 reset_symbolizer_child_pipes (void)
526 if (the_pmu_tool_info->symbolizer_to_pipefd[1] != -1)
528 close (the_pmu_tool_info->symbolizer_to_pipefd[1]);
529 the_pmu_tool_info->symbolizer_to_pipefd[1] = -1;
531 if (the_pmu_tool_info->symbolizer_from_pipefd[0] != -1)
533 close (the_pmu_tool_info->symbolizer_from_pipefd[0]);
534 the_pmu_tool_info->symbolizer_from_pipefd[0] = -1;
539 /* Perform cleanup for the symbolizer process. */
541 static void
542 end_addr2line_symbolizer (void)
544 int pid_status;
545 int wait_status;
546 pid_t pid = the_pmu_tool_info->symbolizer_pid;
548 /* Symbolizer was not running. */
549 if (!pid)
550 return;
552 reset_symbolizer_parent_pipes ();
553 reset_symbolizer_child_pipes ();
554 kill (pid, SIGTERM);
555 wait_status = waitpid (pid, &pid_status, 0);
556 if (sym_debug)
558 if (wait_status == pid)
559 fprintf (stderr, "Normal exit. symbolizer terminated.\n");
560 else
561 fprintf (stderr, "Abnormal exit. symbolizer status, %d.\n", pid_status);
563 the_pmu_tool_info->symbolizer_pid = 0; /* Symoblizer no longer running. */
567 /* Given an address ADDR, return a string containing
568 source_filename:line_num entries. */
570 static char *
571 symbolize_addr2line (void *addr)
573 char buf[32]; /* holds the ascii version of address */
574 int write_count;
575 int read_count;
576 char *srcfile_linenum;
577 size_t max_length = 1024;
579 if (!the_pmu_tool_info->symbolizer_pid)
580 return default_addr2line; /* symbolizer is not running */
582 write_count = snprintf (buf, sizeof (buf), "%p\n", addr);
584 /* Write the address into the pipe. */
585 if (write (the_pmu_tool_info->symbolizer_to_pipefd[1], buf, write_count)
586 < write_count)
588 if (sym_debug)
589 fprintf (stderr, "Cannot write symbolizer pipe.\n");
590 return default_addr2line;
593 srcfile_linenum = XNEWVEC (char, max_length);
594 read_count = read (the_pmu_tool_info->symbolizer_from_pipefd[0],
595 srcfile_linenum, max_length);
596 if (read_count == -1)
598 if (sym_debug)
599 fprintf (stderr, "Cannot read symbolizer pipe.\n");
600 XDELETEVEC (srcfile_linenum);
601 return default_addr2line;
604 srcfile_linenum[read_count] = 0;
605 if (sym_debug)
606 fprintf (stderr, "symbolizer: for address %p, read_count %d, got %s\n",
607 addr, read_count, srcfile_linenum);
608 return srcfile_linenum;
611 /* Start monitoring PPID process via pfmon tool using TMPFILE as a
612 file to store the raw data and using PFMON_ARGS as the command line
613 arguments. */
615 static void
616 start_pfmon_module (pid_t ppid, char *tmpfile, const char **pfmon_args)
618 int i;
619 unsigned int n_args = 0;
620 unsigned n_chars;
621 char pid_buf[64];
622 char filename_buf[1024];
623 char top_n_buf[24];
624 unsigned extra_args;
626 /* Go over the current pfmon args */
627 for (i = 0; i < PMU_TOOL_MAX_ARGS && pfmon_args[i]; ++i)
628 n_args++;
630 if (the_pmu_tool_info->verbose)
631 extra_args = 4; /* account for additional --verbose */
632 else
633 extra_args = 3;
635 /* We are going to add args. */
636 if (n_args >= (PMU_TOOL_MAX_ARGS - extra_args))
638 fprintf (stderr, "too many pfmon args: %d\n", n_args);
639 _exit (0);
642 n_chars = snprintf (pid_buf, sizeof (pid_buf), "--attach-task=%ld",
643 (long)ppid);
644 if (n_chars >= sizeof (pid_buf))
646 fprintf (stderr, "pfmon task id too long: %s\n", pid_buf);
647 return;
649 pfmon_args[n_args] = pid_buf;
650 n_args++;
652 n_chars = snprintf (filename_buf, sizeof (filename_buf), "--smpl-outfile=%s",
653 tmpfile);
654 if (n_chars >= sizeof (filename_buf))
656 fprintf (stderr, "pfmon filename too long: %s\n", filename_buf);
657 return;
659 pfmon_args[n_args] = filename_buf;
660 n_args++;
662 n_chars = snprintf (top_n_buf, sizeof (top_n_buf), "--smpl-show-top=%d",
663 the_pmu_tool_info->top_n_address);
664 if (n_chars >= sizeof (top_n_buf))
666 fprintf (stderr, "pfmon option too long: %s\n", top_n_buf);
667 return;
669 pfmon_args[n_args] = top_n_buf;
670 n_args++;
672 if (the_pmu_tool_info->verbose) {
673 /* Add --verbose as well. */
674 pfmon_args[n_args] = "--verbose";
675 n_args++;
677 pfmon_args[n_args] = (char *)NULL;
679 if (tool_debug)
681 fprintf (stderr, "pfmon args:");
682 for (i = 0; i < PMU_TOOL_MAX_ARGS && pfmon_args[i]; ++i)
683 fprintf (stderr, " %s", pfmon_args[i]);
684 fprintf (stderr, "\n");
686 /* Do execve with NULL env. */
687 execve (pfmon_args[0], (char *const *)pfmon_args, (char * const*)NULL);
688 /* does not return */
691 /* Convert a fractional PCT to an unsigned integer after
692 muliplying by 100. */
694 static unsigned
695 convert_pct_to_unsigned (float pct)
697 return (unsigned)(pct * 100.0f);
700 /* Parse the load latency info pointed by LINE and save it into
701 LL_INFO. Returns 0 if the line was parsed successfully, non-zero
702 otherwise.
704 An example header+line look like these:
705 "counts %self %cum <10 <32 <64 <256 <1024 >=1024
706 %wself code addr symbol"
707 "218 24.06% 24.06% 100.00% 0.00% 0.00% 0.00% 0.00% 0.00% 22.70%
708 0x0000000000413e75 CalcSSIM(...)+965</tmp/psnr>"
711 static int
712 parse_load_latency_line (char *line, gcov_pmu_ll_info_t *ll_info)
714 unsigned counts;
715 /* These are percentages parsed as floats, but then converted to
716 integers after multiplying by 100. */
717 float self, cum, lt_10, lt_32, lt_64, lt_256, lt_1024, gt_1024, wself;
718 long unsigned int p;
719 int n_values;
720 pmu_tool_fns *tool_details = the_pmu_tool_info->tool_details;
722 n_values = sscanf (line, "%u%f%%%f%%%f%%%f%%%f%%%f%%%f%%%f%%%f%%%lx",
723 &counts, &self, &cum, &lt_10, &lt_32, &lt_64, &lt_256,
724 &lt_1024, &gt_1024, &wself, &p);
725 if (n_values != 11)
726 return 1;
728 /* Values read successfully. Do the assignment after converting
729 * percentages into ints. */
730 ll_info->counts = counts;
731 ll_info->self = convert_pct_to_unsigned (self);
732 ll_info->cum = convert_pct_to_unsigned (cum);
733 ll_info->lt_10 = convert_pct_to_unsigned (lt_10);
734 ll_info->lt_32 = convert_pct_to_unsigned (lt_32);
735 ll_info->lt_64 = convert_pct_to_unsigned (lt_64);
736 ll_info->lt_256 = convert_pct_to_unsigned (lt_256);
737 ll_info->lt_1024 = convert_pct_to_unsigned (lt_1024);
738 ll_info->gt_1024 = convert_pct_to_unsigned (gt_1024);
739 ll_info->wself = convert_pct_to_unsigned (wself);
740 ll_info->code_addr = p;
742 /* Run the raw address through the symbolizer. */
743 if (tool_details->symbolize)
745 char *sym_info = tool_details->symbolize ((void *)p);
746 /* sym_info is of the form src_filename:linenum. Descriminator is
747 currently not supported by addr2line. */
748 char *sep = strchr (sym_info, ':');
749 if (!sep)
751 /* Assume entire string is srcfile. */
752 ll_info->filename = (char *)sym_info;
753 ll_info->line = 0;
755 else
757 /* Terminate the filename string at the separator. */
758 *sep = 0;
759 ll_info->filename = (char *)sym_info;
760 /* Convert rest of the sym info to a line number. */
761 ll_info->line = atol (sep+1);
763 ll_info->discriminator = 0;
765 else
767 /* No symbolizer available. */
768 ll_info->filename = NULL;
769 ll_info->line = 0;
770 ll_info->discriminator = 0;
772 return 0;
775 /* Parse the branch mispredict info pointed by LINE and save it into
776 BRM_INFO. Returns 0 if the line was parsed successfully, non-zero
777 otherwise.
779 An example header+line look like these:
780 "counts %self %cum code addr symbol"
781 "6869 37.67% 37.67% 0x00000000004007e5 sum(std::vector<int*,
782 std::allocator<int*> > const&)+51</root/tmp/array>"
785 static int
786 parse_branch_mispredict_line (char *line, gcov_pmu_brm_info_t *brm_info)
788 unsigned counts;
789 /* These are percentages parsed as floats, but then converted to
790 ints after multiplying by 100. */
791 float self, cum;
792 long unsigned int p;
793 int n_values;
794 pmu_tool_fns *tool_details = the_pmu_tool_info->tool_details;
796 n_values = sscanf (line, "%u%f%%%f%%%lx",
797 &counts, &self, &cum, &p);
798 if (n_values != 4)
799 return 1;
801 /* Values read successfully. Do the assignment after converting
802 * percentages into ints. */
803 brm_info->counts = counts;
804 brm_info->self = convert_pct_to_unsigned (self);
805 brm_info->cum = convert_pct_to_unsigned (cum);
806 brm_info->code_addr = p;
808 /* Run the raw address through the symbolizer. */
809 if (tool_details->symbolize)
811 char *sym_info = tool_details->symbolize ((void *)p);
812 /* sym_info is of the form src_filename:linenum. Descriminator is
813 currently not supported by addr2line. */
814 char *sep = strchr (sym_info, ':');
815 if (!sep)
817 /* Assume entire string is srcfile. */
818 brm_info->filename = sym_info;
819 brm_info->line = 0;
821 else
823 /* Terminate the filename string at the separator. */
824 *sep = 0;
825 brm_info->filename = sym_info;
826 /* Convert rest of the sym info to a line number. */
827 brm_info->line = atol (sep+1);
829 brm_info->discriminator = 0;
831 else
833 /* No symbolizer available. */
834 brm_info->filename = NULL;
835 brm_info->line = 0;
836 brm_info->discriminator = 0;
838 return 0;
841 /* Delete load latency info structures INFO. */
843 static void
844 destroy_load_latency_infos (void *info)
846 unsigned i;
847 ll_infos_t* ll_infos = (ll_infos_t *)info;
849 /* delete each element */
850 for (i = 0; i < ll_infos->ll_count; ++i)
851 XDELETE (ll_infos->ll_array[i]);
852 /* delete the array itself */
853 XDELETE (ll_infos->ll_array);
854 __destroy_pmu_tool_header (ll_infos->pmu_tool_header);
855 free (ll_infos->pmu_tool_header);
856 ll_infos->ll_array = 0;
857 ll_infos->ll_count = 0;
860 /* Delete branch mispredict structure INFO. */
862 static void
863 destroy_branch_mispredict_infos (void *info)
865 unsigned i;
866 brm_infos_t* brm_infos = (brm_infos_t *)info;
868 /* delete each element */
869 for (i = 0; i < brm_infos->brm_count; ++i)
870 XDELETE (brm_infos->brm_array[i]);
871 /* delete the array itself */
872 XDELETE (brm_infos->brm_array);
873 __destroy_pmu_tool_header (brm_infos->pmu_tool_header);
874 free (brm_infos->pmu_tool_header);
875 brm_infos->brm_array = 0;
876 brm_infos->brm_count = 0;
879 /* Parse FILENAME for load latency lines into a structure
880 PMU_DATA. Returns 0 on on success. Returns non-zero on
881 failure. */
883 static int
884 parse_pfmon_load_latency (char *filename, void *pmu_data)
886 FILE *fp;
887 size_t buflen = 2*1024;
888 char *buf;
889 ll_infos_t *load_latency_infos = (ll_infos_t *)pmu_data;
890 gcov_pmu_tool_header_t *tool_header = 0;
892 if ((fp = fopen (filename, "r")) == NULL)
894 fprintf (stderr, "cannot open pmu data file: %s\n", filename);
895 return 1;
898 if (!(tool_header = parse_pfmon_tool_header (fp, pfmon_ll_header)))
900 fprintf (stderr, "cannot parse pmu data file header: %s\n", filename);
901 return 1;
904 buf = XNEWVEC (char, buflen);
905 while (fgets (buf, buflen, fp))
907 gcov_pmu_ll_info_t *ll_info = XNEW (gcov_pmu_ll_info_t);
908 if (!parse_load_latency_line (buf, ll_info))
910 /* valid line, add to the array */
911 load_latency_infos->ll_count++;
912 if (load_latency_infos->ll_count >=
913 load_latency_infos->alloc_ll_count)
915 /* need to realloc */
916 load_latency_infos->ll_array =
917 realloc (load_latency_infos->ll_array,
918 2 * load_latency_infos->alloc_ll_count);
919 if (load_latency_infos->ll_array == NULL)
921 fprintf (stderr, "Cannot allocate load latency memory.\n");
922 __destroy_pmu_tool_header (tool_header);
923 free (buf);
924 fclose (fp);
925 return 1;
928 load_latency_infos->ll_array[load_latency_infos->ll_count - 1] =
929 ll_info;
931 else
932 /* Delete invalid line. */
933 XDELETE (ll_info);
935 free (buf);
936 fclose (fp);
937 load_latency_infos->pmu_tool_header = tool_header;
938 return 0;
941 /* Parse open file FP until END_HEADER is seen. The data matching
942 gcov_pmu_tool_header_t fields is saved and returned in a new
943 struct. In case of failure, it returns NULL. */
945 static gcov_pmu_tool_header_t *
946 parse_pfmon_tool_header (FILE *fp, const char *end_header)
948 static const char tag_hostname[] = "# hostname: ";
949 static const char tag_kversion[] = "# kernel version: ";
950 static const char tag_hostcpu[] = "# host CPUs: ";
951 static const char tag_column_desc_start[] = "# description of columns:";
952 static const char tag_column_desc_end[] =
953 "# other columns are self-explanatory";
954 size_t buflen = 4*1024;
955 char *buf, *buf_start, *buf_end;
956 gcov_pmu_tool_header_t *tool_header = XNEWVEC (gcov_pmu_tool_header_t, 1);
957 char *hostname = 0;
958 char *kversion = 0;
959 char *hostcpu = 0;
960 char *column_description = 0;
961 char *column_desc_start = 0;
962 char *column_desc_end = 0;
963 const char *column_header = 0;
964 int got_hostname = 0;
965 int got_kversion = 0 ;
966 int got_hostcpu = 0;
967 int got_end_header = 0;
968 int got_column_description = 0;
970 buf = XNEWVEC (char, buflen);
971 buf_start = buf;
972 buf_end = buf + buflen;
973 while (buf < (buf_end - 1) && fgets (buf, buf_end - buf, fp))
975 if (strncmp (end_header, buf, buf_end - buf) == 0)
977 got_end_header = 1;
978 break;
980 if (!got_hostname &&
981 strncmp (buf, tag_hostname, strlen (tag_hostname)) == 0)
983 size_t len = strlen (buf) - strlen (tag_hostname);
984 hostname = XNEWVEC (char, len);
985 memcpy (hostname, buf + strlen (tag_hostname), len);
986 hostname[len - 1] = 0;
987 tool_header->hostname = hostname;
988 got_hostname = 1;
991 if (!got_kversion &&
992 strncmp (buf, tag_kversion, strlen (tag_kversion)) == 0)
994 size_t len = strlen (buf) - strlen (tag_kversion);
995 kversion = XNEWVEC (char, len);
996 memcpy (kversion, buf + strlen (tag_kversion), len);
997 kversion[len - 1] = 0;
998 tool_header->kernel_version = kversion;
999 got_kversion = 1;
1002 if (!got_hostcpu &&
1003 strncmp (buf, tag_hostcpu, strlen (tag_hostcpu)) == 0)
1005 size_t len = strlen (buf) - strlen (tag_hostcpu);
1006 hostcpu = XNEWVEC (char, len);
1007 memcpy (hostcpu, buf + strlen (tag_hostcpu), len);
1008 hostcpu[len - 1] = 0;
1009 tool_header->host_cpu = hostcpu;
1010 got_hostcpu = 1;
1012 if (!got_column_description &&
1013 strncmp (buf, tag_column_desc_start, strlen (tag_column_desc_start))
1014 == 0)
1016 column_desc_start = buf;
1017 column_desc_end = 0;
1018 /* Continue reading until end of the column descriptor. */
1019 while (buf < (buf_end - 1) && fgets (buf, buf_end - buf, fp))
1021 if (strncmp (buf, tag_column_desc_end,
1022 strlen (tag_column_desc_end)) == 0)
1024 column_desc_end = buf + strlen (tag_column_desc_end);
1025 break;
1027 buf += strlen (buf);
1029 if (column_desc_end)
1031 /* Found the end, copy it into a new string. */
1032 column_description = XNEWVEC (char, column_desc_end -
1033 column_desc_start + 1);
1034 got_column_description = 1;
1035 strcpy (column_description, column_desc_start);
1036 tool_header->column_description = column_description;
1039 buf += strlen (buf);
1042 /* If we are missing any of the fields, return NULL. */
1043 if (!got_end_header || !got_hostname || !got_kversion || !got_hostcpu
1044 || !got_column_description)
1046 free (hostname);
1047 free (kversion);
1048 free (hostcpu);
1049 free (column_description);
1050 free (buf_start);
1051 free (tool_header);
1052 return NULL;
1055 switch (the_pmu_tool_info->event)
1057 case PET_INTEL_LOAD_LATENCY:
1058 case PET_AMD_LOAD_LATENCY:
1059 column_header = pfmon_ll_header;
1060 break;
1061 case PET_INTEL_BRANCH_MISPREDICT:
1062 case PET_AMD_BRANCH_MISPREDICT:
1063 column_header = pfmon_bm_header;
1064 break;
1065 default:
1066 break;
1068 tool_header->column_header = strdup (column_header);
1069 tool_header->full_header = buf_start;
1070 return tool_header;
1074 /* Parse FILENAME for branch mispredict lines into a structure
1075 PMU_DATA. Returns 0 on on success. Returns non-zero on
1076 failure. */
1078 static int
1079 parse_pfmon_branch_mispredicts (char *filename, void *pmu_data)
1081 FILE *fp;
1082 size_t buflen = 2*1024;
1083 char *buf;
1084 brm_infos_t *brm_infos = (brm_infos_t *)pmu_data;
1085 gcov_pmu_tool_header_t *tool_header = 0;
1087 if ((fp = fopen (filename, "r")) == NULL)
1089 fprintf (stderr, "cannot open pmu data file: %s\n", filename);
1090 return 1;
1093 if (!(tool_header = parse_pfmon_tool_header (fp, pfmon_bm_header)))
1095 fprintf (stderr, "cannot parse pmu data file header: %s\n", filename);
1096 return 1;
1099 buf = XNEWVEC (char, buflen);
1100 while (fgets (buf, buflen, fp))
1102 gcov_pmu_brm_info_t *brm = XNEW (gcov_pmu_brm_info_t);
1103 if (!parse_branch_mispredict_line (buf, brm))
1105 /* Valid line, add to the array. */
1106 brm_infos->brm_count++;
1107 if (brm_infos->brm_count >= brm_infos->alloc_brm_count)
1109 /* Do we need to realloc? */
1110 brm_infos->brm_array =
1111 realloc (brm_infos->brm_array,
1112 2 * brm_infos->alloc_brm_count);
1113 if (brm_infos->brm_array == NULL) {
1114 fprintf (stderr,
1115 "Cannot allocate memory for br mispredicts.\n");
1116 __destroy_pmu_tool_header (tool_header);
1117 free (buf);
1118 fclose (fp);
1119 return 1;
1122 brm_infos->brm_array[brm_infos->brm_count - 1] = brm;
1124 else
1125 /* Delete invalid line. */
1126 XDELETE (brm);
1128 free (buf);
1129 fclose (fp);
1130 brm_infos->pmu_tool_header = tool_header;
1131 return 0;
1134 /* Start the monitoring process using pmu tool. Return 0 on success,
1135 non-zero otherwise. */
1137 static int
1138 pmu_start (void)
1140 pid_t pid;
1142 /* no start function */
1143 if (!the_pmu_tool_info->tool_details->start_pmu_module)
1144 return 1;
1146 pid = fork ();
1147 if (pid == -1)
1149 /* error condition */
1150 fprintf (stderr, "Cannot create PMU profiling process, exiting.\n");
1151 return 1;
1153 else if (pid == 0)
1155 /* child */
1156 pid_t ppid = getppid();
1157 char *tmpfile = the_pmu_tool_info->raw_pmu_profile_filename;
1158 const char **pfmon_args = the_pmu_tool_info->tool_details->arg_array;
1159 int new_stderr_fd;
1161 /* Redirect stderr from the child process into a separate file. */
1162 new_stderr_fd = creat (the_pmu_tool_info->tool_stderr_filename,
1163 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
1164 if (new_stderr_fd != -1)
1165 dup2 (new_stderr_fd, 2);
1166 /* The following does an exec and thus is not expected to return. */
1167 the_pmu_tool_info->tool_details->start_pmu_module(ppid, tmpfile,
1168 pfmon_args);
1169 /* exec returned, an error condition. */
1170 fprintf (stderr, "could not create profiling process: %s\n",
1171 the_pmu_tool_info->tool_details->arg_array[0]);
1172 _exit (0);
1174 else
1176 /* parent */
1177 the_pmu_tool_info->pmu_tool_pid = pid;
1178 return 0;
1182 /* Allocate and initialize pmu load latency structure. */
1184 static void *
1185 init_pmu_load_latency (void)
1187 ll_infos_t *load_latency = XNEWVEC (ll_infos_t, 1);
1188 load_latency->ll_count = 0;
1189 load_latency->alloc_ll_count = 64;
1190 load_latency->ll_array = XNEWVEC (gcov_pmu_ll_info_t *,
1191 load_latency->alloc_ll_count);
1192 return (void *)load_latency;
1195 /* Allocate and initialize pmu branch mispredict structure. */
1197 static void *
1198 init_pmu_branch_mispredict (void)
1200 brm_infos_t *brm_info = XNEWVEC (brm_infos_t, 1);
1201 brm_info->brm_count = 0;
1202 brm_info->alloc_brm_count = 64;
1203 brm_info->brm_array = XNEWVEC (gcov_pmu_brm_info_t *,
1204 brm_info->alloc_brm_count);
1205 return (void *)brm_info;
1208 /* Initialize pmu tool based upon PMU_INFO. Sets the appropriate tool
1209 type in the global the_pmu_tool_info. */
1211 static int
1212 init_pmu_tool (struct gcov_pmu_info *pmu_info)
1214 the_pmu_tool_info->pmu_profiling_state = PMU_NONE;
1215 the_pmu_tool_info->verbose = 0;
1216 the_pmu_tool_info->tool = PTT_PFMON; /* we support only pfmon */
1217 the_pmu_tool_info->pmu_tool_pid = 0;
1218 the_pmu_tool_info->top_n_address = pmu_info->pmu_top_n_address;
1219 the_pmu_tool_info->symbolizer_pid = 0;
1220 the_pmu_tool_info->symbolizer_to_pipefd[0] = -1;
1221 the_pmu_tool_info->symbolizer_to_pipefd[1] = -1;
1222 the_pmu_tool_info->symbolizer_from_pipefd[0] = -1;
1223 the_pmu_tool_info->symbolizer_from_pipefd[1] = -1;
1225 if (parse_pmu_profile_options (pmu_info->pmu_tool))
1226 return 1;
1228 if (the_pmu_tool_info->pmu_profiling_state == PMU_ERROR)
1230 fprintf (stderr, "Unsupported PMU module: %s, disabling PMU profiling.\n",
1231 pmu_info->pmu_tool);
1232 return 1;
1235 if (the_pmu_tool_info->tool_details->init_pmu_module)
1236 /* initialize module */
1237 the_pmu_tool_info->pmu_data =
1238 the_pmu_tool_info->tool_details->init_pmu_module();
1239 return 0;
1242 /* Initialize PMU profiling based upon the information passed in
1243 PMU_INFO and use pmu_profile_filename as the file to store the PMU
1244 profile. This is called multiple times from libgcov, once per
1245 object file. We need to make sure to do the necessary
1246 initialization only the first time. For subsequent invocations it
1247 behaves as a NOOP. */
1249 void
1250 __gcov_init_pmu_profiler (struct gcov_pmu_info *pmu_info)
1252 char *raw_pmu_profile_filename;
1253 char *tool_stderr_filename;
1254 if (!pmu_info || !pmu_info->pmu_profile_filename || !pmu_info->pmu_tool)
1255 return;
1257 /* Allocate the global structure on first invocation. */
1258 if (!the_pmu_tool_info)
1260 the_pmu_tool_info = XNEWVEC (struct pmu_tool_info, 1);
1261 if (!the_pmu_tool_info)
1263 fprintf (stderr, "Error allocating memory for PMU tool\n");
1264 return;
1266 if (init_pmu_tool (pmu_info))
1268 /* Initialization error. */
1269 XDELETE (the_pmu_tool_info);
1270 the_pmu_tool_info = 0;
1271 return;
1275 switch (the_pmu_tool_info->pmu_profiling_state)
1277 case PMU_NONE:
1278 the_pmu_tool_info->pmu_profile_filename =
1279 strdup (pmu_info->pmu_profile_filename);
1280 /* Construct an intermediate filename by substituting trailing
1281 '.gcda' with '.pmud'. */
1282 raw_pmu_profile_filename = strdup (pmu_info->pmu_profile_filename);
1283 if (raw_pmu_profile_filename == NULL)
1285 fprintf (stderr, "Cannot allocate memory\n");
1286 exit (1);
1288 strcpy (raw_pmu_profile_filename + strlen (raw_pmu_profile_filename) - 4,
1289 "pmud");
1291 /* Construct a filename for collecting PMU tool's stderr by
1292 substituting trailing '.gcda' with '.stderr'. */
1293 tool_stderr_filename =
1294 XNEWVEC (char, strlen (pmu_info->pmu_profile_filename) + 1 + 2);
1295 strcpy (tool_stderr_filename, pmu_info->pmu_profile_filename);
1296 strcpy (tool_stderr_filename + strlen (tool_stderr_filename) - 4,
1297 "stderr");
1298 the_pmu_tool_info->raw_pmu_profile_filename = raw_pmu_profile_filename;
1299 the_pmu_tool_info->tool_stderr_filename = tool_stderr_filename;
1300 the_pmu_tool_info->pmu_profiling_state = PMU_INITIALIZED;
1301 break;
1303 case PMU_INITIALIZED:
1304 case PMU_OFF:
1305 case PMU_ON:
1306 case PMU_ERROR:
1307 break;
1308 default:
1309 break;
1313 /* Start PMU profiling. It updates the current state. */
1315 void
1316 __gcov_start_pmu_profiler (void)
1318 if (!the_pmu_tool_info)
1319 return;
1321 switch (the_pmu_tool_info->pmu_profiling_state)
1323 case PMU_INITIALIZED:
1324 if (!pmu_start ())
1325 the_pmu_tool_info->pmu_profiling_state = PMU_ON;
1326 else
1327 the_pmu_tool_info->pmu_profiling_state = PMU_ERROR;
1328 break;
1330 case PMU_NONE:
1331 /* PMU was not properly initialized, don't attempt start it. */
1332 the_pmu_tool_info->pmu_profiling_state = PMU_ERROR;
1333 break;
1335 case PMU_OFF:
1336 /* Restarting PMU is not yet supported. */
1337 case PMU_ON:
1338 /* Do nothing. */
1339 case PMU_ERROR:
1340 break;
1342 default:
1343 break;
1347 /* Stop PMU profiling. Currently it doesn't do anything except
1348 bookkeeping. */
1350 void
1351 __gcov_stop_pmu_profiler (void)
1353 if (!the_pmu_tool_info)
1354 return;
1356 if (the_pmu_tool_info->tool_details->stop_pmu_module)
1357 the_pmu_tool_info->tool_details->stop_pmu_module();
1358 if (the_pmu_tool_info->pmu_profiling_state == PMU_ON)
1359 the_pmu_tool_info->pmu_profiling_state = PMU_OFF;
1362 /* Write the load latency information LL_INFO into the gcda file. */
1364 static void
1365 gcov_write_ll_line (const gcov_pmu_ll_info_t *ll_info)
1367 gcov_unsigned_t len = GCOV_TAG_PMU_LOAD_LATENCY_LENGTH (ll_info->filename);
1368 gcov_write_tag_length (GCOV_TAG_PMU_LOAD_LATENCY_INFO, len);
1369 gcov_write_unsigned (ll_info->counts);
1370 gcov_write_unsigned (ll_info->self);
1371 gcov_write_unsigned (ll_info->cum);
1372 gcov_write_unsigned (ll_info->lt_10);
1373 gcov_write_unsigned (ll_info->lt_32);
1374 gcov_write_unsigned (ll_info->lt_64);
1375 gcov_write_unsigned (ll_info->lt_256);
1376 gcov_write_unsigned (ll_info->lt_1024);
1377 gcov_write_unsigned (ll_info->gt_1024);
1378 gcov_write_unsigned (ll_info->wself);
1379 gcov_write_counter (ll_info->code_addr);
1380 gcov_write_unsigned (ll_info->line);
1381 gcov_write_unsigned (ll_info->discriminator);
1382 gcov_write_string (ll_info->filename);
1386 /* Write the branch mispredict information BRM_INFO into the gcda file. */
1388 static void
1389 gcov_write_branch_mispredict_line (const gcov_pmu_brm_info_t *brm_info)
1391 gcov_unsigned_t len = GCOV_TAG_PMU_BRANCH_MISPREDICT_LENGTH (
1392 brm_info->filename);
1393 gcov_write_tag_length (GCOV_TAG_PMU_BRANCH_MISPREDICT_INFO, len);
1394 gcov_write_unsigned (brm_info->counts);
1395 gcov_write_unsigned (brm_info->self);
1396 gcov_write_unsigned (brm_info->cum);
1397 gcov_write_counter (brm_info->code_addr);
1398 gcov_write_unsigned (brm_info->line);
1399 gcov_write_unsigned (brm_info->discriminator);
1400 gcov_write_string (brm_info->filename);
1403 /* Write load latency information INFO into the gcda file. The gcda
1404 file has already been opened and is available for writing. */
1406 static void
1407 gcov_write_load_latency_infos (void *info)
1409 unsigned i;
1410 const ll_infos_t *ll_infos = (const ll_infos_t *)info;
1411 gcov_unsigned_t stamp = 0; /* Don't use stamp as we don't support merge. */
1412 /* We don't support merge, and instead always rewrite the file. But
1413 to rewrite a gcov file we must first read it, however the read
1414 value is ignored. */
1415 gcov_read_unsigned ();
1416 gcov_rewrite ();
1417 gcov_write_tag_length (GCOV_DATA_MAGIC, GCOV_VERSION);
1418 gcov_write_unsigned (stamp);
1419 if (ll_infos->pmu_tool_header)
1420 gcov_write_tool_header (ll_infos->pmu_tool_header);
1421 for (i = 0; i < ll_infos->ll_count; ++i)
1423 /* Write each line. */
1424 gcov_write_ll_line (ll_infos->ll_array[i]);
1426 gcov_truncate ();
1429 /* Write branch mispredict information INFO into the gcda file. The
1430 gcda file has already been opened and is available for writing. */
1432 static void
1433 gcov_write_branch_mispredict_infos (void *info)
1435 unsigned i;
1436 const brm_infos_t *brm_infos = (const brm_infos_t *)info;
1437 gcov_unsigned_t stamp = 0; /* Don't use stamp as we don't support merge. */
1438 /* We don't support merge, and instead always rewrite the file. */
1439 gcov_rewrite ();
1440 gcov_write_tag_length (GCOV_DATA_MAGIC, GCOV_VERSION);
1441 gcov_write_unsigned (stamp);
1442 if (brm_infos->pmu_tool_header)
1443 gcov_write_tool_header (brm_infos->pmu_tool_header);
1444 for (i = 0; i < brm_infos->brm_count; ++i)
1446 /* Write each line. */
1447 gcov_write_branch_mispredict_line (brm_infos->brm_array[i]);
1449 gcov_truncate ();
1452 /* Compute TOOL_HEADER length for writing into the gcov file. */
1454 static gcov_unsigned_t
1455 gcov_tag_pmu_tool_header_length (gcov_pmu_tool_header_t *header)
1457 gcov_unsigned_t len = 0;
1458 if (header)
1460 len += gcov_string_length (header->host_cpu);
1461 len += gcov_string_length (header->hostname);
1462 len += gcov_string_length (header->kernel_version);
1463 len += gcov_string_length (header->column_header);
1464 len += gcov_string_length (header->column_description);
1465 len += gcov_string_length (header->full_header);
1467 return len;
1470 /* Write tool header into the gcda file. It assumes that the gcda file
1471 has already been opened and is available for writing. */
1473 static void
1474 gcov_write_tool_header (gcov_pmu_tool_header_t *header)
1476 gcov_unsigned_t len = gcov_tag_pmu_tool_header_length (header);
1477 gcov_write_tag_length (GCOV_TAG_PMU_TOOL_HEADER, len);
1478 gcov_write_string (header->host_cpu);
1479 gcov_write_string (header->hostname);
1480 gcov_write_string (header->kernel_version);
1481 gcov_write_string (header->column_header);
1482 gcov_write_string (header->column_description);
1483 gcov_write_string (header->full_header);
1487 /* End PMU profiling. If GCDA_ERROR is non-zero then write profiling data into
1488 already open gcda file */
1490 void
1491 __gcov_end_pmu_profiler (int gcda_error)
1493 int pid_status;
1494 int wait_status;
1495 pid_t pid;
1496 pmu_tool_fns *tool_details;
1498 if (!the_pmu_tool_info)
1499 return;
1501 tool_details = the_pmu_tool_info->tool_details;
1502 pid = the_pmu_tool_info->pmu_tool_pid;
1503 if (pid)
1505 if (tool_debug)
1506 fprintf (stderr, "terminating PMU profiling process %ld\n", (long)pid);
1507 kill (pid, SIGTERM);
1508 if (tool_debug)
1509 fprintf (stderr, "parent: waiting for pmu process to end\n");
1510 wait_status = waitpid (pid, &pid_status, 0);
1511 if (tool_debug) {
1512 if (wait_status == pid)
1513 fprintf (stderr, "Normal exit. Child terminated.\n");
1514 else
1515 fprintf (stderr, "Abnormal exit. child status, %d.\n", pid_status);
1519 if (the_pmu_tool_info->pmu_profiling_state != PMU_OFF)
1521 /* nothing to do */
1522 fprintf (stderr,
1523 "__gcov_dump_pmu_profile: incorrect pmu state: %d, pid: %ld\n",
1524 the_pmu_tool_info->pmu_profiling_state,
1525 (unsigned long)pid);
1526 return;
1529 if (!tool_details->parse_pmu_output)
1530 return;
1532 /* Since we are going to parse the output, we also need symbolizer. */
1533 if (tool_details->start_symbolizer)
1534 tool_details->start_symbolizer (getpid ());
1536 if (!tool_details->parse_pmu_output
1537 (the_pmu_tool_info->raw_pmu_profile_filename,
1538 the_pmu_tool_info->pmu_data))
1540 if (!gcda_error && tool_details->gcov_write_pmu_data)
1541 /* Write tool output into the gcda file. */
1542 tool_details->gcov_write_pmu_data (the_pmu_tool_info->pmu_data);
1545 if (tool_details->end_symbolizer)
1546 tool_details->end_symbolizer ();
1548 if (tool_details->cleanup_pmu_data)
1549 tool_details->cleanup_pmu_data (the_pmu_tool_info->pmu_data);
1552 #endif