14e98e3e1Schristos /* Default profiling support. 2*88241920Schristos Copyright (C) 1996-2024 Free Software Foundation, Inc. 34e98e3e1Schristos Contributed by Cygnus Support. 44e98e3e1Schristos 54e98e3e1Schristos This file is part of GDB, the GNU debugger. 64e98e3e1Schristos 74e98e3e1Schristos This program is free software; you can redistribute it and/or modify 84e98e3e1Schristos it under the terms of the GNU General Public License as published by 94e98e3e1Schristos the Free Software Foundation; either version 3 of the License, or 104e98e3e1Schristos (at your option) any later version. 114e98e3e1Schristos 124e98e3e1Schristos This program is distributed in the hope that it will be useful, 134e98e3e1Schristos but WITHOUT ANY WARRANTY; without even the implied warranty of 144e98e3e1Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 154e98e3e1Schristos GNU General Public License for more details. 164e98e3e1Schristos 174e98e3e1Schristos You should have received a copy of the GNU General Public License 184e98e3e1Schristos along with this program. If not, see <http://www.gnu.org/licenses/>. */ 194e98e3e1Schristos 204b169a6bSchristos /* This must come before any other includes. */ 214b169a6bSchristos #include "defs.h" 224b169a6bSchristos 234b169a6bSchristos #include <ctype.h> 244b169a6bSchristos #include <stdarg.h> 254b169a6bSchristos #include <stdlib.h> 264b169a6bSchristos #include <string.h> 274b169a6bSchristos 284b169a6bSchristos #include "ansidecl.h" 294b169a6bSchristos 304e98e3e1Schristos #include "sim-main.h" 314e98e3e1Schristos #include "sim-io.h" 324e98e3e1Schristos #include "sim-options.h" 334e98e3e1Schristos #include "sim-assert.h" 344e98e3e1Schristos 354e98e3e1Schristos #if !WITH_PROFILE_PC_P 364e98e3e1Schristos static unsigned int _profile_stub; 374e98e3e1Schristos # define PROFILE_PC_FREQ(p) _profile_stub 384e98e3e1Schristos # define PROFILE_PC_NR_BUCKETS(p) _profile_stub 394e98e3e1Schristos # define PROFILE_PC_SHIFT(p) _profile_stub 404e98e3e1Schristos # define PROFILE_PC_START(p) _profile_stub 414e98e3e1Schristos # define PROFILE_PC_END(p) _profile_stub 424e98e3e1Schristos # define PROFILE_INSN_COUNT(p) &_profile_stub 434e98e3e1Schristos #endif 444e98e3e1Schristos 454e98e3e1Schristos #define COMMAS(n) sim_add_commas (comma_buf, sizeof (comma_buf), (n)) 464e98e3e1Schristos 474e98e3e1Schristos static MODULE_INIT_FN profile_init; 484e98e3e1Schristos static MODULE_UNINSTALL_FN profile_uninstall; 494e98e3e1Schristos 504e98e3e1Schristos static DECLARE_OPTION_HANDLER (profile_option_handler); 514e98e3e1Schristos 524e98e3e1Schristos enum { 534e98e3e1Schristos OPTION_PROFILE_INSN = OPTION_START, 544e98e3e1Schristos OPTION_PROFILE_MEMORY, 554e98e3e1Schristos OPTION_PROFILE_MODEL, 564e98e3e1Schristos OPTION_PROFILE_FILE, 574e98e3e1Schristos OPTION_PROFILE_CORE, 584e98e3e1Schristos OPTION_PROFILE_CPU_FREQUENCY, 594e98e3e1Schristos OPTION_PROFILE_PC, 604e98e3e1Schristos OPTION_PROFILE_PC_RANGE, 614e98e3e1Schristos OPTION_PROFILE_PC_GRANULARITY, 624e98e3e1Schristos OPTION_PROFILE_RANGE, 634e98e3e1Schristos OPTION_PROFILE_FUNCTION 644e98e3e1Schristos }; 654e98e3e1Schristos 664e98e3e1Schristos static const OPTION profile_options[] = { 674e98e3e1Schristos { {"profile", optional_argument, NULL, 'p'}, 684e98e3e1Schristos 'p', "on|off", "Perform profiling", 694e98e3e1Schristos profile_option_handler, NULL }, 704e98e3e1Schristos { {"profile-insn", optional_argument, NULL, OPTION_PROFILE_INSN}, 714e98e3e1Schristos '\0', "on|off", "Perform instruction profiling", 724e98e3e1Schristos profile_option_handler, NULL }, 734e98e3e1Schristos { {"profile-memory", optional_argument, NULL, OPTION_PROFILE_MEMORY}, 744e98e3e1Schristos '\0', "on|off", "Perform memory profiling", 754e98e3e1Schristos profile_option_handler, NULL }, 764e98e3e1Schristos { {"profile-core", optional_argument, NULL, OPTION_PROFILE_CORE}, 774e98e3e1Schristos '\0', "on|off", "Perform CORE profiling", 784e98e3e1Schristos profile_option_handler, NULL }, 794e98e3e1Schristos { {"profile-model", optional_argument, NULL, OPTION_PROFILE_MODEL}, 804e98e3e1Schristos '\0', "on|off", "Perform model profiling", 814e98e3e1Schristos profile_option_handler, NULL }, 824e98e3e1Schristos { {"profile-cpu-frequency", required_argument, NULL, 834e98e3e1Schristos OPTION_PROFILE_CPU_FREQUENCY}, 844e98e3e1Schristos '\0', "CPU FREQUENCY", "Specify the speed of the simulated cpu clock", 854e98e3e1Schristos profile_option_handler, NULL }, 864e98e3e1Schristos 874e98e3e1Schristos { {"profile-file", required_argument, NULL, OPTION_PROFILE_FILE}, 884e98e3e1Schristos '\0', "FILE NAME", "Specify profile output file", 894e98e3e1Schristos profile_option_handler, NULL }, 904e98e3e1Schristos 914e98e3e1Schristos { {"profile-pc", optional_argument, NULL, OPTION_PROFILE_PC}, 924e98e3e1Schristos '\0', "on|off", "Perform PC profiling", 934e98e3e1Schristos profile_option_handler, NULL }, 944e98e3e1Schristos { {"profile-pc-frequency", required_argument, NULL, 'F'}, 954e98e3e1Schristos 'F', "PC PROFILE FREQUENCY", "Specified PC profiling frequency", 964e98e3e1Schristos profile_option_handler, NULL }, 974e98e3e1Schristos { {"profile-pc-size", required_argument, NULL, 'S'}, 984e98e3e1Schristos 'S', "PC PROFILE SIZE", "Specify PC profiling size", 994e98e3e1Schristos profile_option_handler, NULL }, 1004e98e3e1Schristos { {"profile-pc-granularity", required_argument, NULL, OPTION_PROFILE_PC_GRANULARITY}, 1014e98e3e1Schristos '\0', "PC PROFILE GRANULARITY", "Specify PC profiling sample coverage", 1024e98e3e1Schristos profile_option_handler, NULL }, 1034e98e3e1Schristos { {"profile-pc-range", required_argument, NULL, OPTION_PROFILE_PC_RANGE}, 1044e98e3e1Schristos '\0', "BASE,BOUND", "Specify PC profiling address range", 1054e98e3e1Schristos profile_option_handler, NULL }, 1064e98e3e1Schristos 1074e98e3e1Schristos #ifdef SIM_HAVE_ADDR_RANGE 1084e98e3e1Schristos { {"profile-range", required_argument, NULL, OPTION_PROFILE_RANGE}, 1094e98e3e1Schristos '\0', "START,END", "Specify range of addresses for instruction and model profiling", 1104e98e3e1Schristos profile_option_handler, NULL }, 1114e98e3e1Schristos #if 0 /*wip*/ 1124e98e3e1Schristos { {"profile-function", required_argument, NULL, OPTION_PROFILE_FUNCTION}, 1134e98e3e1Schristos '\0', "FUNCTION", "Specify function to profile", 1144e98e3e1Schristos profile_option_handler, NULL }, 1154e98e3e1Schristos #endif 1164e98e3e1Schristos #endif 1174e98e3e1Schristos 1184e98e3e1Schristos { {NULL, no_argument, NULL, 0}, '\0', NULL, NULL, NULL, NULL } 1194e98e3e1Schristos }; 1204e98e3e1Schristos 1214e98e3e1Schristos /* Set/reset the profile options indicated in MASK. */ 1224e98e3e1Schristos 1234e98e3e1Schristos SIM_RC 1244e98e3e1Schristos set_profile_option_mask (SIM_DESC sd, const char *name, int mask, const char *arg) 1254e98e3e1Schristos { 1264e98e3e1Schristos int profile_nr; 1274e98e3e1Schristos int cpu_nr; 1284e98e3e1Schristos int profile_val = 1; 1294e98e3e1Schristos 1304e98e3e1Schristos if (arg != NULL) 1314e98e3e1Schristos { 1324e98e3e1Schristos if (strcmp (arg, "yes") == 0 1334e98e3e1Schristos || strcmp (arg, "on") == 0 1344e98e3e1Schristos || strcmp (arg, "1") == 0) 1354e98e3e1Schristos profile_val = 1; 1364e98e3e1Schristos else if (strcmp (arg, "no") == 0 1374e98e3e1Schristos || strcmp (arg, "off") == 0 1384e98e3e1Schristos || strcmp (arg, "0") == 0) 1394e98e3e1Schristos profile_val = 0; 1404e98e3e1Schristos else 1414e98e3e1Schristos { 1424e98e3e1Schristos sim_io_eprintf (sd, "Argument `%s' for `--profile%s' invalid, one of `on', `off', `yes', `no' expected\n", arg, name); 1434e98e3e1Schristos return SIM_RC_FAIL; 1444e98e3e1Schristos } 1454e98e3e1Schristos } 1464e98e3e1Schristos 1474e98e3e1Schristos /* update applicable profile bits */ 1484e98e3e1Schristos for (profile_nr = 0; profile_nr < MAX_PROFILE_VALUES; ++profile_nr) 1494e98e3e1Schristos { 1504e98e3e1Schristos if ((mask & (1 << profile_nr)) == 0) 1514e98e3e1Schristos continue; 1524e98e3e1Schristos 1534e98e3e1Schristos #if 0 /* see sim-trace.c, set flags in STATE here if/when there are any */ 1544e98e3e1Schristos /* Set non-cpu specific values. */ 1554e98e3e1Schristos switch (profile_nr) 1564e98e3e1Schristos { 1574e98e3e1Schristos case ??? : 1584e98e3e1Schristos break; 1594e98e3e1Schristos } 1604e98e3e1Schristos #endif 1614e98e3e1Schristos 1624e98e3e1Schristos /* Set cpu values. */ 1634e98e3e1Schristos for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; cpu_nr++) 1644e98e3e1Schristos { 1654e98e3e1Schristos CPU_PROFILE_FLAGS (STATE_CPU (sd, cpu_nr))[profile_nr] = profile_val; 1664e98e3e1Schristos } 1674e98e3e1Schristos } 1684e98e3e1Schristos 1694e98e3e1Schristos /* Re-compute the cpu profile summary. */ 1704e98e3e1Schristos if (profile_val) 1714e98e3e1Schristos { 1724e98e3e1Schristos for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; cpu_nr++) 1734e98e3e1Schristos CPU_PROFILE_DATA (STATE_CPU (sd, cpu_nr))->profile_any_p = 1; 1744e98e3e1Schristos } 1754e98e3e1Schristos else 1764e98e3e1Schristos { 1774e98e3e1Schristos for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; cpu_nr++) 1784e98e3e1Schristos { 1794e98e3e1Schristos CPU_PROFILE_DATA (STATE_CPU (sd, cpu_nr))->profile_any_p = 0; 1804e98e3e1Schristos for (profile_nr = 0; profile_nr < MAX_PROFILE_VALUES; ++profile_nr) 1814e98e3e1Schristos { 1824e98e3e1Schristos if (CPU_PROFILE_FLAGS (STATE_CPU (sd, cpu_nr))[profile_nr]) 1834e98e3e1Schristos { 1844e98e3e1Schristos CPU_PROFILE_DATA (STATE_CPU (sd, cpu_nr))->profile_any_p = 1; 1854e98e3e1Schristos break; 1864e98e3e1Schristos } 1874e98e3e1Schristos } 1884e98e3e1Schristos } 1894e98e3e1Schristos } 1904e98e3e1Schristos 1914e98e3e1Schristos return SIM_RC_OK; 1924e98e3e1Schristos } 1934e98e3e1Schristos 1944e98e3e1Schristos /* Set one profile option based on its IDX value. 1954e98e3e1Schristos Not static as cgen-scache.c uses it. */ 1964e98e3e1Schristos 1974e98e3e1Schristos SIM_RC 1984e98e3e1Schristos sim_profile_set_option (SIM_DESC sd, const char *name, int idx, const char *arg) 1994e98e3e1Schristos { 2004e98e3e1Schristos return set_profile_option_mask (sd, name, 1 << idx, arg); 2014e98e3e1Schristos } 2024e98e3e1Schristos 2034e98e3e1Schristos static SIM_RC 2044e98e3e1Schristos parse_frequency (SIM_DESC sd, const char *arg, unsigned long *freq) 2054e98e3e1Schristos { 2064e98e3e1Schristos const char *ch; 2074e98e3e1Schristos /* First, parse a decimal number. */ 2084e98e3e1Schristos *freq = 0; 2094e98e3e1Schristos ch = arg; 2104e98e3e1Schristos if (isdigit (*arg)) 2114e98e3e1Schristos { 2124e98e3e1Schristos for (/**/; *ch != '\0'; ++ch) 2134e98e3e1Schristos { 2144e98e3e1Schristos if (! isdigit (*ch)) 2154e98e3e1Schristos break; 2164e98e3e1Schristos *freq = *freq * 10 + (*ch - '0'); 2174e98e3e1Schristos } 2184e98e3e1Schristos 2194e98e3e1Schristos /* Accept KHz, MHz or Hz as a suffix. */ 2204e98e3e1Schristos if (tolower (*ch) == 'm') 2214e98e3e1Schristos { 2224e98e3e1Schristos *freq *= 1000000; 2234e98e3e1Schristos ++ch; 2244e98e3e1Schristos } 2254e98e3e1Schristos else if (tolower (*ch) == 'k') 2264e98e3e1Schristos { 2274e98e3e1Schristos *freq *= 1000; 2284e98e3e1Schristos ++ch; 2294e98e3e1Schristos } 2304e98e3e1Schristos 2314e98e3e1Schristos if (tolower (*ch) == 'h') 2324e98e3e1Schristos { 2334e98e3e1Schristos ++ch; 2344e98e3e1Schristos if (tolower (*ch) == 'z') 2354e98e3e1Schristos ++ch; 2364e98e3e1Schristos } 2374e98e3e1Schristos } 2384e98e3e1Schristos 2394e98e3e1Schristos if (*ch != '\0') 2404e98e3e1Schristos { 2414e98e3e1Schristos sim_io_eprintf (sd, "Invalid argument for --profile-cpu-frequency: %s\n", 2424e98e3e1Schristos arg); 2434e98e3e1Schristos *freq = 0; 2444e98e3e1Schristos return SIM_RC_FAIL; 2454e98e3e1Schristos } 2464e98e3e1Schristos 2474e98e3e1Schristos return SIM_RC_OK; 2484e98e3e1Schristos } 2494e98e3e1Schristos 2504e98e3e1Schristos static SIM_RC 2514e98e3e1Schristos profile_option_handler (SIM_DESC sd, 2524e98e3e1Schristos sim_cpu *cpu, 2534e98e3e1Schristos int opt, 2544e98e3e1Schristos char *arg, 2554e98e3e1Schristos int is_command) 2564e98e3e1Schristos { 2574e98e3e1Schristos int cpu_nr; 2584e98e3e1Schristos 2594e98e3e1Schristos /* FIXME: Need to handle `cpu' arg. */ 2604e98e3e1Schristos 2614e98e3e1Schristos switch (opt) 2624e98e3e1Schristos { 2634e98e3e1Schristos case 'p' : 2644e98e3e1Schristos if (! WITH_PROFILE) 2654e98e3e1Schristos sim_io_eprintf (sd, "Profiling not compiled in, `-p' ignored\n"); 2664e98e3e1Schristos else 2674e98e3e1Schristos return set_profile_option_mask (sd, "profile", PROFILE_USEFUL_MASK, 2684e98e3e1Schristos arg); 2694e98e3e1Schristos break; 2704e98e3e1Schristos 2714e98e3e1Schristos case OPTION_PROFILE_INSN : 2724e98e3e1Schristos if (WITH_PROFILE_INSN_P) 2734e98e3e1Schristos return sim_profile_set_option (sd, "-insn", PROFILE_INSN_IDX, arg); 2744e98e3e1Schristos else 2754e98e3e1Schristos sim_io_eprintf (sd, "Instruction profiling not compiled in, `--profile-insn' ignored\n"); 2764e98e3e1Schristos break; 2774e98e3e1Schristos 2784e98e3e1Schristos case OPTION_PROFILE_MEMORY : 2794e98e3e1Schristos if (WITH_PROFILE_MEMORY_P) 2804e98e3e1Schristos return sim_profile_set_option (sd, "-memory", PROFILE_MEMORY_IDX, arg); 2814e98e3e1Schristos else 2824e98e3e1Schristos sim_io_eprintf (sd, "Memory profiling not compiled in, `--profile-memory' ignored\n"); 2834e98e3e1Schristos break; 2844e98e3e1Schristos 2854e98e3e1Schristos case OPTION_PROFILE_CORE : 2864e98e3e1Schristos if (WITH_PROFILE_CORE_P) 2874e98e3e1Schristos return sim_profile_set_option (sd, "-core", PROFILE_CORE_IDX, arg); 2884e98e3e1Schristos else 2894e98e3e1Schristos sim_io_eprintf (sd, "CORE profiling not compiled in, `--profile-core' ignored\n"); 2904e98e3e1Schristos break; 2914e98e3e1Schristos 2924e98e3e1Schristos case OPTION_PROFILE_MODEL : 2934e98e3e1Schristos if (WITH_PROFILE_MODEL_P) 2944e98e3e1Schristos return sim_profile_set_option (sd, "-model", PROFILE_MODEL_IDX, arg); 2954e98e3e1Schristos else 2964e98e3e1Schristos sim_io_eprintf (sd, "Model profiling not compiled in, `--profile-model' ignored\n"); 2974e98e3e1Schristos break; 2984e98e3e1Schristos 2994e98e3e1Schristos case OPTION_PROFILE_CPU_FREQUENCY : 3004e98e3e1Schristos { 3014e98e3e1Schristos unsigned long val; 3024e98e3e1Schristos SIM_RC rc = parse_frequency (sd, arg, &val); 3034e98e3e1Schristos if (rc == SIM_RC_OK) 3044e98e3e1Schristos { 3054e98e3e1Schristos for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; ++cpu_nr) 3064e98e3e1Schristos PROFILE_CPU_FREQ (CPU_PROFILE_DATA (STATE_CPU (sd,cpu_nr))) = val; 3074e98e3e1Schristos } 3084e98e3e1Schristos return rc; 3094e98e3e1Schristos } 3104e98e3e1Schristos 3114e98e3e1Schristos case OPTION_PROFILE_FILE : 3124e98e3e1Schristos /* FIXME: Might want this to apply to pc profiling only, 3134e98e3e1Schristos or have two profile file options. */ 3144e98e3e1Schristos if (! WITH_PROFILE) 3154e98e3e1Schristos sim_io_eprintf (sd, "Profiling not compiled in, `--profile-file' ignored\n"); 3164e98e3e1Schristos else 3174e98e3e1Schristos { 3184e98e3e1Schristos FILE *f = fopen (arg, "w"); 3194e98e3e1Schristos 3204e98e3e1Schristos if (f == NULL) 3214e98e3e1Schristos { 3224e98e3e1Schristos sim_io_eprintf (sd, "Unable to open profile output file `%s'\n", arg); 3234e98e3e1Schristos return SIM_RC_FAIL; 3244e98e3e1Schristos } 3254e98e3e1Schristos for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; ++cpu_nr) 3264e98e3e1Schristos PROFILE_FILE (CPU_PROFILE_DATA (STATE_CPU (sd, cpu_nr))) = f; 3274e98e3e1Schristos } 3284e98e3e1Schristos break; 3294e98e3e1Schristos 3304e98e3e1Schristos case OPTION_PROFILE_PC: 3314e98e3e1Schristos if (WITH_PROFILE_PC_P) 3324e98e3e1Schristos return sim_profile_set_option (sd, "-pc", PROFILE_PC_IDX, arg); 3334e98e3e1Schristos else 3344e98e3e1Schristos sim_io_eprintf (sd, "PC profiling not compiled in, `--profile-pc' ignored\n"); 3354e98e3e1Schristos break; 3364e98e3e1Schristos 3374e98e3e1Schristos case 'F' : 3384e98e3e1Schristos if (WITH_PROFILE_PC_P) 3394e98e3e1Schristos { 3404e98e3e1Schristos /* FIXME: Validate arg. */ 3414e98e3e1Schristos int val = atoi (arg); 3424e98e3e1Schristos for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; ++cpu_nr) 3434e98e3e1Schristos PROFILE_PC_FREQ (CPU_PROFILE_DATA (STATE_CPU (sd, cpu_nr))) = val; 3444e98e3e1Schristos for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; ++cpu_nr) 3454e98e3e1Schristos CPU_PROFILE_FLAGS (STATE_CPU (sd, cpu_nr))[PROFILE_PC_IDX] = 1; 3464e98e3e1Schristos } 3474e98e3e1Schristos else 3484e98e3e1Schristos sim_io_eprintf (sd, "PC profiling not compiled in, `--profile-pc-frequency' ignored\n"); 3494e98e3e1Schristos break; 3504e98e3e1Schristos 3514e98e3e1Schristos case 'S' : 3524e98e3e1Schristos if (WITH_PROFILE_PC_P) 3534e98e3e1Schristos { 3544e98e3e1Schristos /* FIXME: Validate arg. */ 3554e98e3e1Schristos int val = atoi (arg); 3564e98e3e1Schristos for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; ++cpu_nr) 3574e98e3e1Schristos PROFILE_PC_NR_BUCKETS (CPU_PROFILE_DATA (STATE_CPU (sd, cpu_nr))) = val; 3584e98e3e1Schristos for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; ++cpu_nr) 3594e98e3e1Schristos CPU_PROFILE_FLAGS (STATE_CPU (sd, cpu_nr))[PROFILE_PC_IDX] = 1; 3604e98e3e1Schristos } 3614e98e3e1Schristos else 3624e98e3e1Schristos sim_io_eprintf (sd, "PC profiling not compiled in, `--profile-pc-size' ignored\n"); 3634e98e3e1Schristos break; 3644e98e3e1Schristos 3654e98e3e1Schristos case OPTION_PROFILE_PC_GRANULARITY: 3664e98e3e1Schristos if (WITH_PROFILE_PC_P) 3674e98e3e1Schristos { 3684e98e3e1Schristos int shift; 3694e98e3e1Schristos int val = atoi (arg); 3704e98e3e1Schristos /* check that the granularity is a power of two */ 3714e98e3e1Schristos shift = 0; 3724e98e3e1Schristos while (val > (1 << shift)) 3734e98e3e1Schristos { 3744e98e3e1Schristos shift += 1; 3754e98e3e1Schristos } 3764e98e3e1Schristos if (val != (1 << shift)) 3774e98e3e1Schristos { 3784e98e3e1Schristos sim_io_eprintf (sd, "PC profiling granularity not a power of two\n"); 3794e98e3e1Schristos return SIM_RC_FAIL; 3804e98e3e1Schristos } 3814e98e3e1Schristos if (shift == 0) 3824e98e3e1Schristos { 3834e98e3e1Schristos sim_io_eprintf (sd, "PC profiling granularity too small"); 3844e98e3e1Schristos return SIM_RC_FAIL; 3854e98e3e1Schristos } 3864e98e3e1Schristos for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; ++cpu_nr) 3874e98e3e1Schristos PROFILE_PC_SHIFT (CPU_PROFILE_DATA (STATE_CPU (sd, cpu_nr))) = shift; 3884e98e3e1Schristos for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; ++cpu_nr) 3894e98e3e1Schristos CPU_PROFILE_FLAGS (STATE_CPU (sd, cpu_nr))[PROFILE_PC_IDX] = 1; 3904e98e3e1Schristos } 3914e98e3e1Schristos else 3924e98e3e1Schristos sim_io_eprintf (sd, "PC profiling not compiled in, `--profile-pc-granularity' ignored\n"); 3934e98e3e1Schristos break; 3944e98e3e1Schristos 3954e98e3e1Schristos case OPTION_PROFILE_PC_RANGE: 3964e98e3e1Schristos if (WITH_PROFILE_PC_P) 3974e98e3e1Schristos { 3984e98e3e1Schristos /* FIXME: Validate args */ 3994e98e3e1Schristos char *chp = arg; 4004e98e3e1Schristos unsigned long base; 4014e98e3e1Schristos unsigned long bound; 4024e98e3e1Schristos base = strtoul (chp, &chp, 0); 4034e98e3e1Schristos if (*chp != ',') 4044e98e3e1Schristos { 4054e98e3e1Schristos sim_io_eprintf (sd, "--profile-pc-range missing BOUND argument\n"); 4064e98e3e1Schristos return SIM_RC_FAIL; 4074e98e3e1Schristos } 4084e98e3e1Schristos bound = strtoul (chp + 1, NULL, 0); 4094e98e3e1Schristos for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; ++cpu_nr) 4104e98e3e1Schristos { 4114e98e3e1Schristos PROFILE_PC_START (CPU_PROFILE_DATA (STATE_CPU (sd, cpu_nr))) = base; 4124e98e3e1Schristos PROFILE_PC_END (CPU_PROFILE_DATA (STATE_CPU (sd, cpu_nr))) = bound; 4134e98e3e1Schristos } 4144e98e3e1Schristos for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; ++cpu_nr) 4154e98e3e1Schristos CPU_PROFILE_FLAGS (STATE_CPU (sd, cpu_nr))[PROFILE_PC_IDX] = 1; 4164e98e3e1Schristos } 4174e98e3e1Schristos else 4184e98e3e1Schristos sim_io_eprintf (sd, "PC profiling not compiled in, `--profile-pc-range' ignored\n"); 4194e98e3e1Schristos break; 4204e98e3e1Schristos 4214e98e3e1Schristos #ifdef SIM_HAVE_ADDR_RANGE 4224e98e3e1Schristos case OPTION_PROFILE_RANGE : 4234e98e3e1Schristos if (WITH_PROFILE) 4244e98e3e1Schristos { 4254e98e3e1Schristos char *chp = arg; 4264e98e3e1Schristos unsigned long start,end; 4274e98e3e1Schristos start = strtoul (chp, &chp, 0); 4284e98e3e1Schristos if (*chp != ',') 4294e98e3e1Schristos { 4304e98e3e1Schristos sim_io_eprintf (sd, "--profile-range missing END argument\n"); 4314e98e3e1Schristos return SIM_RC_FAIL; 4324e98e3e1Schristos } 4334e98e3e1Schristos end = strtoul (chp + 1, NULL, 0); 4344e98e3e1Schristos /* FIXME: Argument validation. */ 4354e98e3e1Schristos if (cpu != NULL) 4364e98e3e1Schristos sim_addr_range_add (PROFILE_RANGE (CPU_PROFILE_DATA (cpu)), 4374e98e3e1Schristos start, end); 4384e98e3e1Schristos else 4394e98e3e1Schristos for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; ++cpu_nr) 4404e98e3e1Schristos sim_addr_range_add (PROFILE_RANGE (CPU_PROFILE_DATA (STATE_CPU (sd, cpu_nr))), 4414e98e3e1Schristos start, end); 4424e98e3e1Schristos } 4434e98e3e1Schristos else 4444e98e3e1Schristos sim_io_eprintf (sd, "Profiling not compiled in, `--profile-range' ignored\n"); 4454e98e3e1Schristos break; 4464e98e3e1Schristos 4474e98e3e1Schristos case OPTION_PROFILE_FUNCTION : 4484e98e3e1Schristos if (WITH_PROFILE) 4494e98e3e1Schristos { 4504e98e3e1Schristos /*wip: need to compute function range given name*/ 4514e98e3e1Schristos } 4524e98e3e1Schristos else 4534e98e3e1Schristos sim_io_eprintf (sd, "Profiling not compiled in, `--profile-function' ignored\n"); 4544e98e3e1Schristos break; 4554e98e3e1Schristos #endif /* SIM_HAVE_ADDR_RANGE */ 4564e98e3e1Schristos } 4574e98e3e1Schristos 4584e98e3e1Schristos return SIM_RC_OK; 4594e98e3e1Schristos } 4604e98e3e1Schristos 4614e98e3e1Schristos /* Profiling output hooks. */ 4624e98e3e1Schristos 4634b169a6bSchristos static void ATTRIBUTE_PRINTF (3, 0) 4644e98e3e1Schristos profile_vprintf (SIM_DESC sd, sim_cpu *cpu, const char *fmt, va_list ap) 4654e98e3e1Schristos { 4664e98e3e1Schristos FILE *fp = PROFILE_FILE (CPU_PROFILE_DATA (cpu)); 4674e98e3e1Schristos 4684e98e3e1Schristos /* If an output file was given, redirect output to that. */ 4694e98e3e1Schristos if (fp != NULL) 4704e98e3e1Schristos vfprintf (fp, fmt, ap); 4714e98e3e1Schristos else 4724e98e3e1Schristos sim_io_evprintf (sd, fmt, ap); 4734e98e3e1Schristos } 4744e98e3e1Schristos 4754b169a6bSchristos ATTRIBUTE_PRINTF (3, 4) 4764e98e3e1Schristos static void 4774e98e3e1Schristos profile_printf (SIM_DESC sd, sim_cpu *cpu, const char *fmt, ...) 4784e98e3e1Schristos { 4794e98e3e1Schristos va_list ap; 4804e98e3e1Schristos 4814e98e3e1Schristos va_start (ap, fmt); 4824e98e3e1Schristos profile_vprintf (sd, cpu, fmt, ap); 4834e98e3e1Schristos va_end (ap); 4844e98e3e1Schristos } 4854e98e3e1Schristos 4864e98e3e1Schristos /* PC profiling support */ 4874e98e3e1Schristos 4884e98e3e1Schristos #if WITH_PROFILE_PC_P 4894e98e3e1Schristos 4904e98e3e1Schristos static void 4914e98e3e1Schristos profile_pc_cleanup (SIM_DESC sd) 4924e98e3e1Schristos { 4934e98e3e1Schristos int n; 4944e98e3e1Schristos for (n = 0; n < MAX_NR_PROCESSORS; n++) 4954e98e3e1Schristos { 4964e98e3e1Schristos sim_cpu *cpu = STATE_CPU (sd, n); 4974e98e3e1Schristos PROFILE_DATA *data = CPU_PROFILE_DATA (cpu); 4984e98e3e1Schristos if (PROFILE_PC_COUNT (data) != NULL) 4994e98e3e1Schristos free (PROFILE_PC_COUNT (data)); 5004e98e3e1Schristos PROFILE_PC_COUNT (data) = NULL; 5014e98e3e1Schristos if (PROFILE_PC_EVENT (data) != NULL) 5024e98e3e1Schristos sim_events_deschedule (sd, PROFILE_PC_EVENT (data)); 5034e98e3e1Schristos PROFILE_PC_EVENT (data) = NULL; 5044e98e3e1Schristos } 5054e98e3e1Schristos } 5064e98e3e1Schristos 5074e98e3e1Schristos 5084e98e3e1Schristos static void 5094e98e3e1Schristos profile_pc_uninstall (SIM_DESC sd) 5104e98e3e1Schristos { 5114e98e3e1Schristos profile_pc_cleanup (sd); 5124e98e3e1Schristos } 5134e98e3e1Schristos 5144e98e3e1Schristos static void 5154e98e3e1Schristos profile_pc_event (SIM_DESC sd, 5164e98e3e1Schristos void *data) 5174e98e3e1Schristos { 5184e98e3e1Schristos sim_cpu *cpu = (sim_cpu*) data; 5194e98e3e1Schristos PROFILE_DATA *profile = CPU_PROFILE_DATA (cpu); 520212397c6Schristos address_word pc = sim_pc_get (cpu); 5214e98e3e1Schristos unsigned i; 5224e98e3e1Schristos i = (pc - PROFILE_PC_START (profile)) >> PROFILE_PC_SHIFT (profile); 5234e98e3e1Schristos if (i < PROFILE_PC_NR_BUCKETS (profile)) 5244e98e3e1Schristos PROFILE_PC_COUNT (profile) [i] += 1; /* Overflow? */ 5254e98e3e1Schristos else 5264e98e3e1Schristos PROFILE_PC_COUNT (profile) [PROFILE_PC_NR_BUCKETS (profile)] += 1; 5274e98e3e1Schristos PROFILE_PC_EVENT (profile) = 5284e98e3e1Schristos sim_events_schedule (sd, PROFILE_PC_FREQ (profile), profile_pc_event, cpu); 5294e98e3e1Schristos } 5304e98e3e1Schristos 5314e98e3e1Schristos static SIM_RC 5324e98e3e1Schristos profile_pc_init (SIM_DESC sd) 5334e98e3e1Schristos { 5344e98e3e1Schristos int n; 5354e98e3e1Schristos profile_pc_cleanup (sd); 5364e98e3e1Schristos for (n = 0; n < MAX_NR_PROCESSORS; n++) 5374e98e3e1Schristos { 5384e98e3e1Schristos sim_cpu *cpu = STATE_CPU (sd, n); 5394e98e3e1Schristos PROFILE_DATA *data = CPU_PROFILE_DATA (cpu); 540212397c6Schristos if (CPU_PROFILE_FLAGS (STATE_CPU (sd, n))[PROFILE_PC_IDX]) 5414e98e3e1Schristos { 5424e98e3e1Schristos int bucket_size; 5434e98e3e1Schristos /* fill in the frequency if not specified */ 5444e98e3e1Schristos if (PROFILE_PC_FREQ (data) == 0) 5454e98e3e1Schristos PROFILE_PC_FREQ (data) = 257; 5464e98e3e1Schristos /* fill in the start/end if not specified */ 5474e98e3e1Schristos if (PROFILE_PC_END (data) == 0) 5484e98e3e1Schristos { 5494e98e3e1Schristos PROFILE_PC_START (data) = STATE_TEXT_START (sd); 5504e98e3e1Schristos PROFILE_PC_END (data) = STATE_TEXT_END (sd); 5514e98e3e1Schristos } 5524e98e3e1Schristos /* Compute the number of buckets if not specified. */ 5534e98e3e1Schristos if (PROFILE_PC_NR_BUCKETS (data) == 0) 5544e98e3e1Schristos { 5554e98e3e1Schristos if (PROFILE_PC_BUCKET_SIZE (data) == 0) 5564e98e3e1Schristos PROFILE_PC_NR_BUCKETS (data) = 16; 5574e98e3e1Schristos else 5584e98e3e1Schristos { 5594e98e3e1Schristos if (PROFILE_PC_END (data) == 0) 5604e98e3e1Schristos { 5614e98e3e1Schristos /* nr_buckets = (full-address-range / 2) / (bucket_size / 2) */ 5624e98e3e1Schristos PROFILE_PC_NR_BUCKETS (data) = 5634b169a6bSchristos ((1ULL << sizeof (sim_cia) * (8 - 1)) 5644e98e3e1Schristos / (PROFILE_PC_BUCKET_SIZE (data) / 2)); 5654e98e3e1Schristos } 5664e98e3e1Schristos else 5674e98e3e1Schristos { 5684e98e3e1Schristos PROFILE_PC_NR_BUCKETS (data) = 5694e98e3e1Schristos ((PROFILE_PC_END (data) 5704e98e3e1Schristos - PROFILE_PC_START (data) 5714e98e3e1Schristos + PROFILE_PC_BUCKET_SIZE (data) - 1) 5724e98e3e1Schristos / PROFILE_PC_BUCKET_SIZE (data)); 5734e98e3e1Schristos } 5744e98e3e1Schristos } 5754e98e3e1Schristos } 5764e98e3e1Schristos /* Compute the bucket size if not specified. Ensure that it 5774e98e3e1Schristos is rounded up to the next power of two */ 5784e98e3e1Schristos if (PROFILE_PC_BUCKET_SIZE (data) == 0) 5794e98e3e1Schristos { 5804e98e3e1Schristos if (PROFILE_PC_END (data) == 0) 5814e98e3e1Schristos /* bucket_size = (full-address-range / 2) / (nr_buckets / 2) */ 5824b169a6bSchristos bucket_size = ((1ULL << ((sizeof (sim_cia) * 8) - 1)) 5834e98e3e1Schristos / (PROFILE_PC_NR_BUCKETS (data) / 2)); 5844e98e3e1Schristos else 5854e98e3e1Schristos bucket_size = ((PROFILE_PC_END (data) 5864e98e3e1Schristos - PROFILE_PC_START (data) 5874e98e3e1Schristos + PROFILE_PC_NR_BUCKETS (data) - 1) 5884e98e3e1Schristos / PROFILE_PC_NR_BUCKETS (data)); 5894e98e3e1Schristos PROFILE_PC_SHIFT (data) = 0; 5904e98e3e1Schristos while (bucket_size > PROFILE_PC_BUCKET_SIZE (data)) 5914e98e3e1Schristos { 5924e98e3e1Schristos PROFILE_PC_SHIFT (data) += 1; 5934e98e3e1Schristos } 5944e98e3e1Schristos } 5954e98e3e1Schristos /* Align the end address with bucket size */ 5964e98e3e1Schristos if (PROFILE_PC_END (data) != 0) 5974e98e3e1Schristos PROFILE_PC_END (data) = (PROFILE_PC_START (data) 5984e98e3e1Schristos + (PROFILE_PC_BUCKET_SIZE (data) 5994e98e3e1Schristos * PROFILE_PC_NR_BUCKETS (data))); 6004e98e3e1Schristos /* create the relevant buffers */ 6014e98e3e1Schristos PROFILE_PC_COUNT (data) = 6024e98e3e1Schristos NZALLOC (unsigned, PROFILE_PC_NR_BUCKETS (data) + 1); 6034e98e3e1Schristos PROFILE_PC_EVENT (data) = 6044e98e3e1Schristos sim_events_schedule (sd, 6054e98e3e1Schristos PROFILE_PC_FREQ (data), 6064e98e3e1Schristos profile_pc_event, 6074e98e3e1Schristos cpu); 6084e98e3e1Schristos } 6094e98e3e1Schristos } 6104e98e3e1Schristos return SIM_RC_OK; 6114e98e3e1Schristos } 6124e98e3e1Schristos 6134e98e3e1Schristos static void 614*88241920Schristos profile_print_pc (sim_cpu *cpu, bool verbose) 6154e98e3e1Schristos { 6164e98e3e1Schristos SIM_DESC sd = CPU_STATE (cpu); 6174e98e3e1Schristos PROFILE_DATA *profile = CPU_PROFILE_DATA (cpu); 6184e98e3e1Schristos char comma_buf[20]; 6194e98e3e1Schristos unsigned max_val; 6204e98e3e1Schristos unsigned total; 6214e98e3e1Schristos unsigned i; 6224e98e3e1Schristos 6234e98e3e1Schristos if (PROFILE_PC_COUNT (profile) == 0) 6244e98e3e1Schristos return; 6254e98e3e1Schristos 6264e98e3e1Schristos profile_printf (sd, cpu, "Program Counter Statistics:\n\n"); 6274e98e3e1Schristos 6284e98e3e1Schristos /* First pass over data computes various things. */ 6294e98e3e1Schristos max_val = 0; 6304e98e3e1Schristos total = 0; 6314e98e3e1Schristos for (i = 0; i <= PROFILE_PC_NR_BUCKETS (profile); ++i) 6324e98e3e1Schristos { 6334e98e3e1Schristos total += PROFILE_PC_COUNT (profile) [i]; 6344e98e3e1Schristos if (PROFILE_PC_COUNT (profile) [i] > max_val) 6354e98e3e1Schristos max_val = PROFILE_PC_COUNT (profile) [i]; 6364e98e3e1Schristos } 6374e98e3e1Schristos 6384e98e3e1Schristos profile_printf (sd, cpu, " Total samples: %s\n", 6394e98e3e1Schristos COMMAS (total)); 6404e98e3e1Schristos profile_printf (sd, cpu, " Granularity: %s bytes per bucket\n", 6414e98e3e1Schristos COMMAS (PROFILE_PC_BUCKET_SIZE (profile))); 6424e98e3e1Schristos profile_printf (sd, cpu, " Size: %s buckets\n", 6434e98e3e1Schristos COMMAS (PROFILE_PC_NR_BUCKETS (profile))); 6444e98e3e1Schristos profile_printf (sd, cpu, " Frequency: %s cycles per sample\n", 6454e98e3e1Schristos COMMAS (PROFILE_PC_FREQ (profile))); 6464e98e3e1Schristos 6474e98e3e1Schristos if (PROFILE_PC_END (profile) != 0) 6484e98e3e1Schristos profile_printf (sd, cpu, " Range: 0x%lx 0x%lx\n", 6494e98e3e1Schristos (long) PROFILE_PC_START (profile), 6504e98e3e1Schristos (long) PROFILE_PC_END (profile)); 6514e98e3e1Schristos 6524e98e3e1Schristos if (verbose && max_val != 0) 6534e98e3e1Schristos { 6544e98e3e1Schristos /* Now we can print the histogram. */ 6554e98e3e1Schristos profile_printf (sd, cpu, "\n"); 6564e98e3e1Schristos for (i = 0; i <= PROFILE_PC_NR_BUCKETS (profile); ++i) 6574e98e3e1Schristos { 6584e98e3e1Schristos if (PROFILE_PC_COUNT (profile) [i] != 0) 6594e98e3e1Schristos { 6604e98e3e1Schristos profile_printf (sd, cpu, " "); 6614e98e3e1Schristos if (i == PROFILE_PC_NR_BUCKETS (profile)) 6624e98e3e1Schristos profile_printf (sd, cpu, "%10s:", "overflow"); 6634e98e3e1Schristos else 6644e98e3e1Schristos profile_printf (sd, cpu, "0x%08lx:", 6654e98e3e1Schristos (long) (PROFILE_PC_START (profile) 6664e98e3e1Schristos + (i * PROFILE_PC_BUCKET_SIZE (profile)))); 6674e98e3e1Schristos profile_printf (sd, cpu, " %*s", 6684e98e3e1Schristos max_val < 10000 ? 5 : 10, 6694e98e3e1Schristos COMMAS (PROFILE_PC_COUNT (profile) [i])); 6704e98e3e1Schristos profile_printf (sd, cpu, " %4.1f", 6714e98e3e1Schristos (PROFILE_PC_COUNT (profile) [i] * 100.0) / total); 6724e98e3e1Schristos profile_printf (sd, cpu, ": "); 6734e98e3e1Schristos sim_profile_print_bar (sd, cpu, PROFILE_HISTOGRAM_WIDTH, 6744e98e3e1Schristos PROFILE_PC_COUNT (profile) [i], 6754e98e3e1Schristos max_val); 6764e98e3e1Schristos profile_printf (sd, cpu, "\n"); 6774e98e3e1Schristos } 6784e98e3e1Schristos } 6794e98e3e1Schristos } 6804e98e3e1Schristos 6814e98e3e1Schristos /* dump the histogram to the file "gmon.out" using BSD's gprof file 6824e98e3e1Schristos format */ 6834e98e3e1Schristos /* Since a profile data file is in the native format of the host on 6844e98e3e1Schristos which the profile is being, endian issues are not considered in 6854e98e3e1Schristos the code below. */ 6864e98e3e1Schristos /* FIXME: Is this the best place for this code? */ 6874e98e3e1Schristos { 6884e98e3e1Schristos FILE *pf = fopen ("gmon.out", "wb"); 6894e98e3e1Schristos 6904e98e3e1Schristos if (pf == NULL) 6914e98e3e1Schristos sim_io_eprintf (sd, "Failed to open \"gmon.out\" profile file\n"); 6924e98e3e1Schristos else 6934e98e3e1Schristos { 6944e98e3e1Schristos int ok; 6954e98e3e1Schristos /* FIXME: what if the target has a 64 bit PC? */ 6964b169a6bSchristos uint32_t header[3]; 6974e98e3e1Schristos unsigned loop; 6984e98e3e1Schristos if (PROFILE_PC_END (profile) != 0) 6994e98e3e1Schristos { 7004e98e3e1Schristos header[0] = PROFILE_PC_START (profile); 7014e98e3e1Schristos header[1] = PROFILE_PC_END (profile); 7024e98e3e1Schristos } 7034e98e3e1Schristos else 7044e98e3e1Schristos { 7054e98e3e1Schristos header[0] = 0; 7064e98e3e1Schristos header[1] = 0; 7074e98e3e1Schristos } 7084e98e3e1Schristos /* size of sample buffer (+ header) */ 7094e98e3e1Schristos header[2] = PROFILE_PC_NR_BUCKETS (profile) * 2 + sizeof (header); 7104e98e3e1Schristos 7114e98e3e1Schristos /* Header must be written out in target byte order. */ 7124e98e3e1Schristos H2T (header[0]); 7134e98e3e1Schristos H2T (header[1]); 7144e98e3e1Schristos H2T (header[2]); 7154e98e3e1Schristos 7164e98e3e1Schristos ok = fwrite (&header, sizeof (header), 1, pf); 7174e98e3e1Schristos for (loop = 0; 7184e98e3e1Schristos ok && (loop < PROFILE_PC_NR_BUCKETS (profile)); 7194e98e3e1Schristos loop++) 7204e98e3e1Schristos { 7214b169a6bSchristos int16_t sample; 7224e98e3e1Schristos if (PROFILE_PC_COUNT (profile) [loop] >= 0xffff) 7234e98e3e1Schristos sample = 0xffff; 7244e98e3e1Schristos else 7254e98e3e1Schristos sample = PROFILE_PC_COUNT (profile) [loop]; 7264e98e3e1Schristos H2T (sample); 7274e98e3e1Schristos ok = fwrite (&sample, sizeof (sample), 1, pf); 7284e98e3e1Schristos } 7294e98e3e1Schristos if (ok == 0) 7304e98e3e1Schristos sim_io_eprintf (sd, "Failed to write to \"gmon.out\" profile file\n"); 7314e98e3e1Schristos fclose (pf); 7324e98e3e1Schristos } 7334e98e3e1Schristos } 7344e98e3e1Schristos 7354e98e3e1Schristos profile_printf (sd, cpu, "\n"); 7364e98e3e1Schristos } 7374e98e3e1Schristos 7384e98e3e1Schristos #endif 7394e98e3e1Schristos 7404e98e3e1Schristos /* Summary printing support. */ 7414e98e3e1Schristos 7424e98e3e1Schristos #if WITH_PROFILE_INSN_P 7434e98e3e1Schristos 7444e98e3e1Schristos static SIM_RC 7454e98e3e1Schristos profile_insn_init (SIM_DESC sd) 7464e98e3e1Schristos { 7474e98e3e1Schristos int c; 7484e98e3e1Schristos 7494e98e3e1Schristos for (c = 0; c < MAX_NR_PROCESSORS; ++c) 7504e98e3e1Schristos { 7514e98e3e1Schristos sim_cpu *cpu = STATE_CPU (sd, c); 7524e98e3e1Schristos 7534e98e3e1Schristos if (CPU_MAX_INSNS (cpu) > 0) 7544e98e3e1Schristos PROFILE_INSN_COUNT (CPU_PROFILE_DATA (cpu)) = NZALLOC (unsigned int, CPU_MAX_INSNS (cpu)); 7554e98e3e1Schristos } 7564e98e3e1Schristos 7574e98e3e1Schristos return SIM_RC_OK; 7584e98e3e1Schristos } 7594e98e3e1Schristos 7604e98e3e1Schristos static void 761*88241920Schristos profile_print_insn (sim_cpu *cpu, bool verbose) 7624e98e3e1Schristos { 7634e98e3e1Schristos unsigned int i, n, total, max_val, max_name_len; 7644e98e3e1Schristos SIM_DESC sd = CPU_STATE (cpu); 7654e98e3e1Schristos PROFILE_DATA *data = CPU_PROFILE_DATA (cpu); 7664e98e3e1Schristos char comma_buf[20]; 7674e98e3e1Schristos 7684e98e3e1Schristos /* If MAX_INSNS not set, insn profiling isn't supported. */ 7694e98e3e1Schristos if (CPU_MAX_INSNS (cpu) == 0) 7704e98e3e1Schristos return; 7714e98e3e1Schristos 7724e98e3e1Schristos profile_printf (sd, cpu, "Instruction Statistics"); 7734e98e3e1Schristos #ifdef SIM_HAVE_ADDR_RANGE 7744e98e3e1Schristos if (PROFILE_RANGE (data)->ranges) 7754e98e3e1Schristos profile_printf (sd, cpu, " (for selected address range(s))"); 7764e98e3e1Schristos #endif 7774e98e3e1Schristos profile_printf (sd, cpu, "\n\n"); 7784e98e3e1Schristos 7794e98e3e1Schristos /* First pass over data computes various things. */ 7804e98e3e1Schristos max_val = 0; 7814e98e3e1Schristos total = 0; 7824e98e3e1Schristos max_name_len = 0; 7834e98e3e1Schristos for (i = 0; i < CPU_MAX_INSNS (cpu); ++i) 7844e98e3e1Schristos { 7854e98e3e1Schristos const char *name = (*CPU_INSN_NAME (cpu)) (cpu, i); 7864e98e3e1Schristos 7874e98e3e1Schristos if (name == NULL) 7884e98e3e1Schristos continue; 7894e98e3e1Schristos total += PROFILE_INSN_COUNT (data) [i]; 7904e98e3e1Schristos if (PROFILE_INSN_COUNT (data) [i] > max_val) 7914e98e3e1Schristos max_val = PROFILE_INSN_COUNT (data) [i]; 7924e98e3e1Schristos n = strlen (name); 7934e98e3e1Schristos if (n > max_name_len) 7944e98e3e1Schristos max_name_len = n; 7954e98e3e1Schristos } 7964e98e3e1Schristos /* set the total insn count, in case client is being lazy */ 7974e98e3e1Schristos if (! PROFILE_TOTAL_INSN_COUNT (data)) 7984e98e3e1Schristos PROFILE_TOTAL_INSN_COUNT (data) = total; 7994e98e3e1Schristos 8004e98e3e1Schristos profile_printf (sd, cpu, " Total: %s insns\n", COMMAS (total)); 8014e98e3e1Schristos 8024e98e3e1Schristos if (verbose && max_val != 0) 8034e98e3e1Schristos { 8044e98e3e1Schristos /* Now we can print the histogram. */ 8054e98e3e1Schristos profile_printf (sd, cpu, "\n"); 8064e98e3e1Schristos for (i = 0; i < CPU_MAX_INSNS (cpu); ++i) 8074e98e3e1Schristos { 8084e98e3e1Schristos const char *name = (*CPU_INSN_NAME (cpu)) (cpu, i); 8094e98e3e1Schristos 8104e98e3e1Schristos if (name == NULL) 8114e98e3e1Schristos continue; 8124e98e3e1Schristos if (PROFILE_INSN_COUNT (data) [i] != 0) 8134e98e3e1Schristos { 8144e98e3e1Schristos profile_printf (sd, cpu, " %*s: %*s: ", 8154e98e3e1Schristos max_name_len, name, 8164e98e3e1Schristos max_val < 10000 ? 5 : 10, 8174e98e3e1Schristos COMMAS (PROFILE_INSN_COUNT (data) [i])); 8184e98e3e1Schristos sim_profile_print_bar (sd, cpu, PROFILE_HISTOGRAM_WIDTH, 8194e98e3e1Schristos PROFILE_INSN_COUNT (data) [i], 8204e98e3e1Schristos max_val); 8214e98e3e1Schristos profile_printf (sd, cpu, "\n"); 8224e98e3e1Schristos } 8234e98e3e1Schristos } 8244e98e3e1Schristos } 8254e98e3e1Schristos 8264e98e3e1Schristos profile_printf (sd, cpu, "\n"); 8274e98e3e1Schristos } 8284e98e3e1Schristos 8294e98e3e1Schristos #endif 8304e98e3e1Schristos 8314e98e3e1Schristos #if WITH_PROFILE_MEMORY_P 8324e98e3e1Schristos 8334e98e3e1Schristos static void 834*88241920Schristos profile_print_memory (sim_cpu *cpu, bool verbose) 8354e98e3e1Schristos { 8364e98e3e1Schristos unsigned int i, n; 8374e98e3e1Schristos unsigned int total_read, total_write; 8384e98e3e1Schristos unsigned int max_val, max_name_len; 8394e98e3e1Schristos /* FIXME: Need to add smp support. */ 8404e98e3e1Schristos SIM_DESC sd = CPU_STATE (cpu); 8414e98e3e1Schristos PROFILE_DATA *data = CPU_PROFILE_DATA (cpu); 8424e98e3e1Schristos char comma_buf[20]; 8434e98e3e1Schristos 8444e98e3e1Schristos profile_printf (sd, cpu, "Memory Access Statistics\n\n"); 8454e98e3e1Schristos 8464e98e3e1Schristos /* First pass over data computes various things. */ 8474e98e3e1Schristos max_val = total_read = total_write = max_name_len = 0; 8484e98e3e1Schristos for (i = 0; i < MODE_TARGET_MAX; ++i) 8494e98e3e1Schristos { 8504e98e3e1Schristos total_read += PROFILE_READ_COUNT (data) [i]; 8514e98e3e1Schristos total_write += PROFILE_WRITE_COUNT (data) [i]; 8524e98e3e1Schristos if (PROFILE_READ_COUNT (data) [i] > max_val) 8534e98e3e1Schristos max_val = PROFILE_READ_COUNT (data) [i]; 8544e98e3e1Schristos if (PROFILE_WRITE_COUNT (data) [i] > max_val) 8554e98e3e1Schristos max_val = PROFILE_WRITE_COUNT (data) [i]; 8564e98e3e1Schristos n = strlen (MODE_NAME (i)); 8574e98e3e1Schristos if (n > max_name_len) 8584e98e3e1Schristos max_name_len = n; 8594e98e3e1Schristos } 8604e98e3e1Schristos 8614e98e3e1Schristos /* One could use PROFILE_LABEL_WIDTH here. I chose not to. */ 8624e98e3e1Schristos profile_printf (sd, cpu, " Total read: %s accesses\n", 8634e98e3e1Schristos COMMAS (total_read)); 8644e98e3e1Schristos profile_printf (sd, cpu, " Total write: %s accesses\n", 8654e98e3e1Schristos COMMAS (total_write)); 8664e98e3e1Schristos 8674e98e3e1Schristos if (verbose && max_val != 0) 8684e98e3e1Schristos { 8694e98e3e1Schristos /* FIXME: Need to separate instruction fetches from data fetches 8704e98e3e1Schristos as the former swamps the latter. */ 8714e98e3e1Schristos /* Now we can print the histogram. */ 8724e98e3e1Schristos profile_printf (sd, cpu, "\n"); 8734e98e3e1Schristos for (i = 0; i < MODE_TARGET_MAX; ++i) 8744e98e3e1Schristos { 8754e98e3e1Schristos if (PROFILE_READ_COUNT (data) [i] != 0) 8764e98e3e1Schristos { 8774e98e3e1Schristos profile_printf (sd, cpu, " %*s read: %*s: ", 8784e98e3e1Schristos max_name_len, MODE_NAME (i), 8794e98e3e1Schristos max_val < 10000 ? 5 : 10, 8804e98e3e1Schristos COMMAS (PROFILE_READ_COUNT (data) [i])); 8814e98e3e1Schristos sim_profile_print_bar (sd, cpu, PROFILE_HISTOGRAM_WIDTH, 8824e98e3e1Schristos PROFILE_READ_COUNT (data) [i], 8834e98e3e1Schristos max_val); 8844e98e3e1Schristos profile_printf (sd, cpu, "\n"); 8854e98e3e1Schristos } 8864e98e3e1Schristos if (PROFILE_WRITE_COUNT (data) [i] != 0) 8874e98e3e1Schristos { 8884e98e3e1Schristos profile_printf (sd, cpu, " %*s write: %*s: ", 8894e98e3e1Schristos max_name_len, MODE_NAME (i), 8904e98e3e1Schristos max_val < 10000 ? 5 : 10, 8914e98e3e1Schristos COMMAS (PROFILE_WRITE_COUNT (data) [i])); 8924e98e3e1Schristos sim_profile_print_bar (sd, cpu, PROFILE_HISTOGRAM_WIDTH, 8934e98e3e1Schristos PROFILE_WRITE_COUNT (data) [i], 8944e98e3e1Schristos max_val); 8954e98e3e1Schristos profile_printf (sd, cpu, "\n"); 8964e98e3e1Schristos } 8974e98e3e1Schristos } 8984e98e3e1Schristos } 8994e98e3e1Schristos 9004e98e3e1Schristos profile_printf (sd, cpu, "\n"); 9014e98e3e1Schristos } 9024e98e3e1Schristos 9034e98e3e1Schristos #endif 9044e98e3e1Schristos 9054e98e3e1Schristos #if WITH_PROFILE_CORE_P 9064e98e3e1Schristos 9074e98e3e1Schristos static void 908*88241920Schristos profile_print_core (sim_cpu *cpu, bool verbose) 9094e98e3e1Schristos { 9104e98e3e1Schristos unsigned int total; 9114e98e3e1Schristos unsigned int max_val; 9124e98e3e1Schristos /* FIXME: Need to add smp support. */ 9134e98e3e1Schristos SIM_DESC sd = CPU_STATE (cpu); 9144e98e3e1Schristos PROFILE_DATA *data = CPU_PROFILE_DATA (cpu); 9154e98e3e1Schristos char comma_buf[20]; 9164e98e3e1Schristos 9174e98e3e1Schristos profile_printf (sd, cpu, "CORE Statistics\n\n"); 9184e98e3e1Schristos 9194e98e3e1Schristos /* First pass over data computes various things. */ 9204e98e3e1Schristos { 9214e98e3e1Schristos unsigned map; 9224e98e3e1Schristos total = 0; 9234e98e3e1Schristos max_val = 0; 9244e98e3e1Schristos for (map = 0; map < nr_maps; map++) 9254e98e3e1Schristos { 9264e98e3e1Schristos total += PROFILE_CORE_COUNT (data) [map]; 9274e98e3e1Schristos if (PROFILE_CORE_COUNT (data) [map] > max_val) 9284e98e3e1Schristos max_val = PROFILE_CORE_COUNT (data) [map]; 9294e98e3e1Schristos } 9304e98e3e1Schristos } 9314e98e3e1Schristos 9324e98e3e1Schristos /* One could use PROFILE_LABEL_WIDTH here. I chose not to. */ 9334e98e3e1Schristos profile_printf (sd, cpu, " Total: %s accesses\n", 9344e98e3e1Schristos COMMAS (total)); 9354e98e3e1Schristos 9364e98e3e1Schristos if (verbose && max_val != 0) 9374e98e3e1Schristos { 9384e98e3e1Schristos unsigned map; 9394e98e3e1Schristos /* Now we can print the histogram. */ 9404e98e3e1Schristos profile_printf (sd, cpu, "\n"); 9414e98e3e1Schristos for (map = 0; map < nr_maps; map++) 9424e98e3e1Schristos { 9434e98e3e1Schristos if (PROFILE_CORE_COUNT (data) [map] != 0) 9444e98e3e1Schristos { 9454e98e3e1Schristos profile_printf (sd, cpu, "%10s:", map_to_str (map)); 9464e98e3e1Schristos profile_printf (sd, cpu, "%*s: ", 9474e98e3e1Schristos max_val < 10000 ? 5 : 10, 9484e98e3e1Schristos COMMAS (PROFILE_CORE_COUNT (data) [map])); 9494e98e3e1Schristos sim_profile_print_bar (sd, cpu, PROFILE_HISTOGRAM_WIDTH, 9504e98e3e1Schristos PROFILE_CORE_COUNT (data) [map], 9514e98e3e1Schristos max_val); 9524e98e3e1Schristos profile_printf (sd, cpu, "\n"); 9534e98e3e1Schristos } 9544e98e3e1Schristos } 9554e98e3e1Schristos } 9564e98e3e1Schristos 9574e98e3e1Schristos profile_printf (sd, cpu, "\n"); 9584e98e3e1Schristos } 9594e98e3e1Schristos 9604e98e3e1Schristos #endif 9614e98e3e1Schristos 9624e98e3e1Schristos #if WITH_PROFILE_MODEL_P 9634e98e3e1Schristos 9644e98e3e1Schristos static void 965*88241920Schristos profile_print_model (sim_cpu *cpu, bool verbose) 9664e98e3e1Schristos { 9674e98e3e1Schristos SIM_DESC sd = CPU_STATE (cpu); 9684e98e3e1Schristos PROFILE_DATA *data = CPU_PROFILE_DATA (cpu); 9694e98e3e1Schristos unsigned long cti_stall_cycles = PROFILE_MODEL_CTI_STALL_CYCLES (data); 9704e98e3e1Schristos unsigned long load_stall_cycles = PROFILE_MODEL_LOAD_STALL_CYCLES (data); 9714e98e3e1Schristos unsigned long total_cycles = PROFILE_MODEL_TOTAL_CYCLES (data); 9724e98e3e1Schristos char comma_buf[20]; 9734e98e3e1Schristos 9744e98e3e1Schristos profile_printf (sd, cpu, "Model %s Timing Information", 9754e98e3e1Schristos MODEL_NAME (CPU_MODEL (cpu))); 9764e98e3e1Schristos #ifdef SIM_HAVE_ADDR_RANGE 9774e98e3e1Schristos if (PROFILE_RANGE (data)->ranges) 9784e98e3e1Schristos profile_printf (sd, cpu, " (for selected address range(s))"); 9794e98e3e1Schristos #endif 9804e98e3e1Schristos profile_printf (sd, cpu, "\n\n"); 9814e98e3e1Schristos profile_printf (sd, cpu, " %-*s %s\n", 9824e98e3e1Schristos PROFILE_LABEL_WIDTH, "Taken branches:", 9834e98e3e1Schristos COMMAS (PROFILE_MODEL_TAKEN_COUNT (data))); 9844e98e3e1Schristos profile_printf (sd, cpu, " %-*s %s\n", 9854e98e3e1Schristos PROFILE_LABEL_WIDTH, "Untaken branches:", 9864e98e3e1Schristos COMMAS (PROFILE_MODEL_UNTAKEN_COUNT (data))); 9874e98e3e1Schristos profile_printf (sd, cpu, " %-*s %s\n", 9884e98e3e1Schristos PROFILE_LABEL_WIDTH, "Cycles stalled due to branches:", 9894e98e3e1Schristos COMMAS (cti_stall_cycles)); 9904e98e3e1Schristos profile_printf (sd, cpu, " %-*s %s\n", 9914e98e3e1Schristos PROFILE_LABEL_WIDTH, "Cycles stalled due to loads:", 9924e98e3e1Schristos COMMAS (load_stall_cycles)); 9934e98e3e1Schristos profile_printf (sd, cpu, " %-*s %s\n", 9944e98e3e1Schristos PROFILE_LABEL_WIDTH, "Total cycles (*approximate*):", 9954e98e3e1Schristos COMMAS (total_cycles)); 9964e98e3e1Schristos profile_printf (sd, cpu, "\n"); 9974e98e3e1Schristos } 9984e98e3e1Schristos 9994e98e3e1Schristos #endif 10004e98e3e1Schristos 10014e98e3e1Schristos void 10024e98e3e1Schristos sim_profile_print_bar (SIM_DESC sd, sim_cpu *cpu, unsigned int width, 10034e98e3e1Schristos unsigned int val, unsigned int max_val) 10044e98e3e1Schristos { 10054e98e3e1Schristos unsigned int i, count; 10064e98e3e1Schristos 10074e98e3e1Schristos count = ((double) val / (double) max_val) * (double) width; 10084e98e3e1Schristos 10094e98e3e1Schristos for (i = 0; i < count; ++i) 10104e98e3e1Schristos profile_printf (sd, cpu, "*"); 10114e98e3e1Schristos } 10124e98e3e1Schristos 10134e98e3e1Schristos /* Print the simulator's execution speed for CPU. */ 10144e98e3e1Schristos 10154e98e3e1Schristos static void 10164e98e3e1Schristos profile_print_speed (sim_cpu *cpu) 10174e98e3e1Schristos { 10184e98e3e1Schristos SIM_DESC sd = CPU_STATE (cpu); 10194e98e3e1Schristos PROFILE_DATA *data = CPU_PROFILE_DATA (cpu); 10204e98e3e1Schristos unsigned long milliseconds = sim_events_elapsed_time (sd); 10214e98e3e1Schristos unsigned long total = PROFILE_TOTAL_INSN_COUNT (data); 10224e98e3e1Schristos double clock; 10234e98e3e1Schristos double secs; 10244e98e3e1Schristos char comma_buf[20]; 10254e98e3e1Schristos 10264e98e3e1Schristos profile_printf (sd, cpu, "Simulator Execution Speed\n\n"); 10274e98e3e1Schristos 10284e98e3e1Schristos if (total != 0) 10294e98e3e1Schristos profile_printf (sd, cpu, " Total instructions: %s\n", COMMAS (total)); 10304e98e3e1Schristos 10314e98e3e1Schristos if (milliseconds < 1000) 10324e98e3e1Schristos profile_printf (sd, cpu, " Total execution time: < 1 second\n\n"); 10334e98e3e1Schristos else 10344e98e3e1Schristos { 10354e98e3e1Schristos /* The printing of the time rounded to 2 decimal places makes the speed 10364e98e3e1Schristos calculation seem incorrect [even though it is correct]. So round 10374e98e3e1Schristos MILLISECONDS first. This can marginally affect the result, but it's 10384e98e3e1Schristos better that the user not perceive there's a math error. */ 10394e98e3e1Schristos secs = (double) milliseconds / 1000; 10404e98e3e1Schristos secs = ((double) (unsigned long) (secs * 100 + .5)) / 100; 10414e98e3e1Schristos profile_printf (sd, cpu, " Total execution time : %.2f seconds\n", secs); 10424e98e3e1Schristos /* Don't confuse things with data that isn't useful. 10434e98e3e1Schristos If we ran for less than 2 seconds, only use the data if we 10444e98e3e1Schristos executed more than 100,000 insns. */ 10454e98e3e1Schristos if (secs >= 2 || total >= 100000) 10464e98e3e1Schristos profile_printf (sd, cpu, " Simulator speed: %s insns/second\n", 10474e98e3e1Schristos COMMAS ((unsigned long) ((double) total / secs))); 10484e98e3e1Schristos } 10494e98e3e1Schristos 10504e98e3e1Schristos /* Print simulated execution time if the cpu frequency has been specified. */ 10514e98e3e1Schristos clock = PROFILE_CPU_FREQ (data); 10524e98e3e1Schristos if (clock != 0) 10534e98e3e1Schristos { 10544e98e3e1Schristos if (clock >= 1000000) 10554e98e3e1Schristos profile_printf (sd, cpu, " Simulated cpu frequency: %.2f MHz\n", 10564e98e3e1Schristos clock / 1000000); 10574e98e3e1Schristos else 10584e98e3e1Schristos profile_printf (sd, cpu, " Simulated cpu frequency: %.2f Hz\n", clock); 10594e98e3e1Schristos 10604e98e3e1Schristos #if WITH_PROFILE_MODEL_P 10614e98e3e1Schristos if (PROFILE_FLAGS (data) [PROFILE_MODEL_IDX]) 10624e98e3e1Schristos { 10634e98e3e1Schristos /* The printing of the time rounded to 2 decimal places makes the 10644e98e3e1Schristos speed calculation seem incorrect [even though it is correct]. 10654e98e3e1Schristos So round SECS first. This can marginally affect the result, 10664e98e3e1Schristos but it's better that the user not perceive there's a math 10674e98e3e1Schristos error. */ 10684e98e3e1Schristos secs = PROFILE_MODEL_TOTAL_CYCLES (data) / clock; 10694e98e3e1Schristos secs = ((double) (unsigned long) (secs * 100 + .5)) / 100; 10704e98e3e1Schristos profile_printf (sd, cpu, " Simulated execution time: %.2f seconds\n", 10714e98e3e1Schristos secs); 10724e98e3e1Schristos } 10734e98e3e1Schristos #endif /* WITH_PROFILE_MODEL_P */ 10744e98e3e1Schristos } 10754e98e3e1Schristos } 10764e98e3e1Schristos 10774e98e3e1Schristos #ifdef SIM_HAVE_ADDR_RANGE 10784e98e3e1Schristos /* Print selected address ranges. */ 10794e98e3e1Schristos 10804e98e3e1Schristos static void 10814e98e3e1Schristos profile_print_addr_ranges (sim_cpu *cpu) 10824e98e3e1Schristos { 10834e98e3e1Schristos ADDR_SUBRANGE *asr = PROFILE_RANGE (CPU_PROFILE_DATA (cpu))->ranges; 10844e98e3e1Schristos SIM_DESC sd = CPU_STATE (cpu); 10854e98e3e1Schristos 10864e98e3e1Schristos if (asr) 10874e98e3e1Schristos { 10884e98e3e1Schristos profile_printf (sd, cpu, "Selected address ranges\n\n"); 10894e98e3e1Schristos while (asr != NULL) 10904e98e3e1Schristos { 10914e98e3e1Schristos profile_printf (sd, cpu, " 0x%lx - 0x%lx\n", 10924e98e3e1Schristos (long) asr->start, (long) asr->end); 10934e98e3e1Schristos asr = asr->next; 10944e98e3e1Schristos } 10954e98e3e1Schristos profile_printf (sd, cpu, "\n"); 10964e98e3e1Schristos } 10974e98e3e1Schristos } 10984e98e3e1Schristos #endif 10994e98e3e1Schristos 11004e98e3e1Schristos /* Top level function to print all summary profile information. 11014e98e3e1Schristos It is [currently] intended that all such data is printed by this function. 11024e98e3e1Schristos I'd rather keep it all in one place for now. To that end, MISC_CPU and 11034e98e3e1Schristos MISC are callbacks used to print any miscellaneous data. 11044e98e3e1Schristos 11054e98e3e1Schristos One might want to add a user option that allows printing by type or by cpu 11064e98e3e1Schristos (i.e. print all insn data for each cpu first, or print data cpu by cpu). 11074e98e3e1Schristos This may be a case of featuritis so it's currently left out. 11084e98e3e1Schristos 11094e98e3e1Schristos Note that results are indented two spaces to distinguish them from 11104e98e3e1Schristos section titles. */ 11114e98e3e1Schristos 11124e98e3e1Schristos static void 1113*88241920Schristos profile_info (SIM_DESC sd, bool verbose) 11144e98e3e1Schristos { 11154e98e3e1Schristos int i,c; 11164e98e3e1Schristos int print_title_p = 0; 11174e98e3e1Schristos 11184e98e3e1Schristos /* Only print the title if some data has been collected. */ 11194e98e3e1Schristos /* ??? Why don't we just exit if no data collected? */ 11204e98e3e1Schristos /* FIXME: If the number of processors can be selected on the command line, 11214e98e3e1Schristos then MAX_NR_PROCESSORS will need to take an argument of `sd'. */ 11224e98e3e1Schristos 1123a2e2270fSchristos for (c = 0; c < MAX_NR_PROCESSORS && !print_title_p; ++c) 11244e98e3e1Schristos { 11254e98e3e1Schristos sim_cpu *cpu = STATE_CPU (sd, c); 11264e98e3e1Schristos PROFILE_DATA *data = CPU_PROFILE_DATA (cpu); 11274e98e3e1Schristos 11284e98e3e1Schristos for (i = 0; i < MAX_PROFILE_VALUES; ++i) 11294e98e3e1Schristos if (PROFILE_FLAGS (data) [i]) 11304e98e3e1Schristos { 11314e98e3e1Schristos profile_printf (sd, cpu, "Summary profiling results:\n\n"); 11324e98e3e1Schristos print_title_p = 1; 1133a2e2270fSchristos break; 11344e98e3e1Schristos } 11354e98e3e1Schristos } 11364e98e3e1Schristos 11374e98e3e1Schristos /* Loop, cpu by cpu, printing results. */ 11384e98e3e1Schristos 11394e98e3e1Schristos for (c = 0; c < MAX_NR_PROCESSORS; ++c) 11404e98e3e1Schristos { 11414e98e3e1Schristos sim_cpu *cpu = STATE_CPU (sd, c); 11424e98e3e1Schristos PROFILE_DATA *data = CPU_PROFILE_DATA (cpu); 11434e98e3e1Schristos 11444e98e3e1Schristos if (MAX_NR_PROCESSORS > 1 11454e98e3e1Schristos && (0 11464e98e3e1Schristos #if WITH_PROFILE_INSN_P 11474e98e3e1Schristos || PROFILE_FLAGS (data) [PROFILE_INSN_IDX] 11484e98e3e1Schristos #endif 11494e98e3e1Schristos #if WITH_PROFILE_MEMORY_P 11504e98e3e1Schristos || PROFILE_FLAGS (data) [PROFILE_MEMORY_IDX] 11514e98e3e1Schristos #endif 11524e98e3e1Schristos #if WITH_PROFILE_CORE_P 11534e98e3e1Schristos || PROFILE_FLAGS (data) [PROFILE_CORE_IDX] 11544e98e3e1Schristos #endif 11554e98e3e1Schristos #if WITH_PROFILE_MODEL_P 11564e98e3e1Schristos || PROFILE_FLAGS (data) [PROFILE_MODEL_IDX] 11574e98e3e1Schristos #endif 11584b169a6bSchristos #if WITH_PROFILE_SCACHE_P && WITH_SCACHE && defined(CGEN_ARCH) 11594e98e3e1Schristos || PROFILE_FLAGS (data) [PROFILE_SCACHE_IDX] 11604e98e3e1Schristos #endif 11614e98e3e1Schristos #if WITH_PROFILE_PC_P 11624e98e3e1Schristos || PROFILE_FLAGS (data) [PROFILE_PC_IDX] 11634e98e3e1Schristos #endif 11644e98e3e1Schristos )) 11654e98e3e1Schristos { 11664e98e3e1Schristos profile_printf (sd, cpu, "CPU %d\n\n", c); 11674e98e3e1Schristos } 11684e98e3e1Schristos 11694e98e3e1Schristos #ifdef SIM_HAVE_ADDR_RANGE 11704e98e3e1Schristos if (print_title_p 11714e98e3e1Schristos && (PROFILE_INSN_P (cpu) 11724e98e3e1Schristos || PROFILE_MODEL_P (cpu))) 11734e98e3e1Schristos profile_print_addr_ranges (cpu); 11744e98e3e1Schristos #endif 11754e98e3e1Schristos 11764e98e3e1Schristos #if WITH_PROFILE_INSN_P 11774e98e3e1Schristos if (PROFILE_FLAGS (data) [PROFILE_INSN_IDX]) 11784e98e3e1Schristos profile_print_insn (cpu, verbose); 11794e98e3e1Schristos #endif 11804e98e3e1Schristos 11814e98e3e1Schristos #if WITH_PROFILE_MEMORY_P 11824e98e3e1Schristos if (PROFILE_FLAGS (data) [PROFILE_MEMORY_IDX]) 11834e98e3e1Schristos profile_print_memory (cpu, verbose); 11844e98e3e1Schristos #endif 11854e98e3e1Schristos 11864e98e3e1Schristos #if WITH_PROFILE_CORE_P 11874e98e3e1Schristos if (PROFILE_FLAGS (data) [PROFILE_CORE_IDX]) 11884e98e3e1Schristos profile_print_core (cpu, verbose); 11894e98e3e1Schristos #endif 11904e98e3e1Schristos 11914e98e3e1Schristos #if WITH_PROFILE_MODEL_P 11924e98e3e1Schristos if (PROFILE_FLAGS (data) [PROFILE_MODEL_IDX]) 11934e98e3e1Schristos profile_print_model (cpu, verbose); 11944e98e3e1Schristos #endif 11954e98e3e1Schristos 11964b169a6bSchristos #if WITH_PROFILE_SCACHE_P && WITH_SCACHE && defined(CGEN_ARCH) 11974e98e3e1Schristos if (PROFILE_FLAGS (data) [PROFILE_SCACHE_IDX]) 11984e98e3e1Schristos scache_print_profile (cpu, verbose); 11994e98e3e1Schristos #endif 12004e98e3e1Schristos 12014e98e3e1Schristos #if WITH_PROFILE_PC_P 12024e98e3e1Schristos if (PROFILE_FLAGS (data) [PROFILE_PC_IDX]) 12034e98e3e1Schristos profile_print_pc (cpu, verbose); 12044e98e3e1Schristos #endif 12054e98e3e1Schristos 12064e98e3e1Schristos /* Print cpu-specific data before the execution speed. */ 12074e98e3e1Schristos if (PROFILE_INFO_CPU_CALLBACK (data) != NULL) 12084e98e3e1Schristos PROFILE_INFO_CPU_CALLBACK (data) (cpu, verbose); 12094e98e3e1Schristos 12104e98e3e1Schristos /* Always try to print execution time and speed. */ 12114e98e3e1Schristos if (verbose 12124e98e3e1Schristos || PROFILE_FLAGS (data) [PROFILE_INSN_IDX]) 12134e98e3e1Schristos profile_print_speed (cpu); 12144e98e3e1Schristos } 12154e98e3e1Schristos 12164e98e3e1Schristos /* Finally print non-cpu specific miscellaneous data. */ 12174e98e3e1Schristos if (STATE_PROFILE_INFO_CALLBACK (sd)) 12184e98e3e1Schristos STATE_PROFILE_INFO_CALLBACK (sd) (sd, verbose); 12194e98e3e1Schristos 12204e98e3e1Schristos } 12214e98e3e1Schristos 12224b169a6bSchristos /* Provide a prototype to silence -Wmissing-prototypes. */ 12234b169a6bSchristos SIM_RC sim_install_profile (SIM_DESC sd); 12244e98e3e1Schristos 12254b169a6bSchristos /* Install profiling support in the simulator. */ 12264e98e3e1Schristos SIM_RC 12274b169a6bSchristos sim_install_profile (SIM_DESC sd) 12284e98e3e1Schristos { 12294e98e3e1Schristos int i; 12304e98e3e1Schristos 12314e98e3e1Schristos SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER); 12324e98e3e1Schristos sim_add_option_table (sd, NULL, profile_options); 12334e98e3e1Schristos for (i = 0; i < MAX_NR_PROCESSORS; ++i) 12344e98e3e1Schristos memset (CPU_PROFILE_DATA (STATE_CPU (sd, i)), 0, 12354e98e3e1Schristos sizeof (* CPU_PROFILE_DATA (STATE_CPU (sd, i)))); 12364e98e3e1Schristos #if WITH_PROFILE_INSN_P 12374e98e3e1Schristos sim_module_add_init_fn (sd, profile_insn_init); 12384e98e3e1Schristos #endif 12394e98e3e1Schristos #if WITH_PROFILE_PC_P 12404e98e3e1Schristos sim_module_add_uninstall_fn (sd, profile_pc_uninstall); 12414e98e3e1Schristos sim_module_add_init_fn (sd, profile_pc_init); 12424e98e3e1Schristos #endif 12434e98e3e1Schristos sim_module_add_init_fn (sd, profile_init); 12444e98e3e1Schristos sim_module_add_uninstall_fn (sd, profile_uninstall); 12454e98e3e1Schristos sim_module_add_info_fn (sd, profile_info); 12464e98e3e1Schristos return SIM_RC_OK; 12474e98e3e1Schristos } 12484e98e3e1Schristos 12494e98e3e1Schristos static SIM_RC 12504e98e3e1Schristos profile_init (SIM_DESC sd) 12514e98e3e1Schristos { 12524e98e3e1Schristos #ifdef SIM_HAVE_ADDR_RANGE 12534e98e3e1Schristos /* Check if a range has been specified without specifying what to 12544e98e3e1Schristos collect. */ 12554e98e3e1Schristos { 12564e98e3e1Schristos int i; 12574e98e3e1Schristos 12584e98e3e1Schristos for (i = 0; i < MAX_NR_PROCESSORS; ++i) 12594e98e3e1Schristos { 12604e98e3e1Schristos sim_cpu *cpu = STATE_CPU (sd, i); 12614e98e3e1Schristos 12624e98e3e1Schristos if (ADDR_RANGE_RANGES (PROFILE_RANGE (CPU_PROFILE_DATA (cpu))) 12634e98e3e1Schristos && ! (PROFILE_INSN_P (cpu) 12644e98e3e1Schristos || PROFILE_MODEL_P (cpu))) 12654e98e3e1Schristos { 12664e98e3e1Schristos sim_io_eprintf_cpu (cpu, "Profiling address range specified without --profile-insn or --profile-model.\n"); 12674e98e3e1Schristos sim_io_eprintf_cpu (cpu, "Address range ignored.\n"); 12684e98e3e1Schristos sim_addr_range_delete (PROFILE_RANGE (CPU_PROFILE_DATA (cpu)), 12694e98e3e1Schristos 0, ~ (address_word) 0); 12704e98e3e1Schristos } 12714e98e3e1Schristos } 12724e98e3e1Schristos } 12734e98e3e1Schristos #endif 12744e98e3e1Schristos 12754e98e3e1Schristos return SIM_RC_OK; 12764e98e3e1Schristos } 12774e98e3e1Schristos 12784e98e3e1Schristos static void 12794e98e3e1Schristos profile_uninstall (SIM_DESC sd) 12804e98e3e1Schristos { 12814e98e3e1Schristos int i,j; 12824e98e3e1Schristos 12834e98e3e1Schristos for (i = 0; i < MAX_NR_PROCESSORS; ++i) 12844e98e3e1Schristos { 12854e98e3e1Schristos sim_cpu *cpu = STATE_CPU (sd, i); 12864e98e3e1Schristos PROFILE_DATA *data = CPU_PROFILE_DATA (cpu); 12874e98e3e1Schristos 12884e98e3e1Schristos if (PROFILE_FILE (data) != NULL) 12894e98e3e1Schristos { 12904e98e3e1Schristos /* If output from different cpus is going to the same file, 12914e98e3e1Schristos avoid closing the file twice. */ 12924e98e3e1Schristos for (j = 0; j < i; ++j) 12934e98e3e1Schristos if (PROFILE_FILE (CPU_PROFILE_DATA (STATE_CPU (sd, j))) 12944e98e3e1Schristos == PROFILE_FILE (data)) 12954e98e3e1Schristos break; 12964e98e3e1Schristos if (i == j) 12974e98e3e1Schristos fclose (PROFILE_FILE (data)); 12984e98e3e1Schristos } 12994e98e3e1Schristos 13004e98e3e1Schristos if (PROFILE_INSN_COUNT (data) != NULL) 13014e98e3e1Schristos free (PROFILE_INSN_COUNT (data)); 13024e98e3e1Schristos } 13034e98e3e1Schristos } 1304