1 1.1.1.4 christos /* Copyright (C) 2021-2026 Free Software Foundation, Inc. 2 1.1 christos Contributed by Oracle. 3 1.1 christos 4 1.1 christos This file is part of GNU Binutils. 5 1.1 christos 6 1.1 christos This program is free software; you can redistribute it and/or modify 7 1.1 christos it under the terms of the GNU General Public License as published by 8 1.1 christos the Free Software Foundation; either version 3, or (at your option) 9 1.1 christos any later version. 10 1.1 christos 11 1.1 christos This program is distributed in the hope that it will be useful, 12 1.1 christos but WITHOUT ANY WARRANTY; without even the implied warranty of 13 1.1 christos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 1.1 christos GNU General Public License for more details. 15 1.1 christos 16 1.1 christos You should have received a copy of the GNU General Public License 17 1.1 christos along with this program; if not, write to the Free Software 18 1.1 christos Foundation, 51 Franklin Street - Fifth Floor, Boston, 19 1.1 christos MA 02110-1301, USA. */ 20 1.1 christos 21 1.1 christos /* 22 1.1 christos * Profile handling 23 1.1 christos * 24 1.1 christos * Note: SIGPROF signal-handling and interval timer (once exclusive to 25 1.1 christos * profile handling) are now common services provided by the dispatcher. 26 1.1 christos */ 27 1.1 christos 28 1.1 christos #include "config.h" 29 1.1 christos #include <dlfcn.h> 30 1.1.1.2 christos #include <stddef.h> 31 1.1 christos #include <stdlib.h> 32 1.1 christos #include <string.h> 33 1.1 christos #include <ucontext.h> 34 1.1 christos #include <unistd.h> 35 1.1 christos 36 1.1 christos #include "gp-defs.h" 37 1.1 christos #include "collector_module.h" 38 1.1 christos #include "gp-experiment.h" 39 1.1 christos #include "data_pckts.h" 40 1.1 christos #include "libcol_util.h" 41 1.1 christos #include "hwprofile.h" 42 1.1 christos #include "tsd.h" 43 1.1 christos 44 1.1 christos static int init_interface (CollectorInterface*); 45 1.1 christos static int open_experiment (const char *); 46 1.1 christos static int start_data_collection (void); 47 1.1 christos static int stop_data_collection (void); 48 1.1 christos static int close_experiment (void); 49 1.1 christos static int detach_experiment (void); 50 1.1 christos 51 1.1 christos static ModuleInterface module_interface ={ 52 1.1 christos SP_PROFILE_FILE, /* description */ 53 1.1 christos init_interface, /* initInterface */ 54 1.1 christos open_experiment, /* openExperiment */ 55 1.1 christos start_data_collection, /* startDataCollection */ 56 1.1 christos stop_data_collection, /* stopDataCollection */ 57 1.1 christos close_experiment, /* closeExperiment */ 58 1.1 christos detach_experiment /* detachExperiment (fork child) */ 59 1.1 christos }; 60 1.1 christos 61 1.1 christos static CollectorInterface *collector_interface = NULL; 62 1.1 christos static int prof_mode = 0; 63 1.1 christos static CollectorModule prof_hndl = COLLECTOR_MODULE_ERR; 64 1.1 christos static unsigned prof_key = COLLECTOR_TSD_INVALID_KEY; 65 1.1 christos 66 1.1 christos typedef struct ClockPacket 67 1.1 christos { /* clock profiling packet */ 68 1.1 christos CM_Packet comm; 69 1.1 christos pthread_t lwp_id; 70 1.1 christos pthread_t thr_id; 71 1.1 christos uint32_t cpu_id; 72 1.1 christos hrtime_t tstamp __attribute__ ((packed)); 73 1.1 christos uint64_t frinfo __attribute__ ((packed)); 74 1.1 christos int mstate; /* kernel microstate */ 75 1.1 christos int nticks; /* number of ticks in that state */ 76 1.1 christos } ClockPacket; 77 1.1 christos 78 1.1 christos /* XXX should be able to use local types */ 79 1.1 christos #define CLOCK_TYPE OPROF_PCKT 80 1.1 christos 81 1.1 christos #define CHCK_REENTRANCE(x) ( !prof_mode || ((x) = collector_interface->getKey( prof_key )) == NULL || (*(x) != 0) ) 82 1.1 christos #define PUSH_REENTRANCE(x) ((*(x))++) 83 1.1 christos #define POP_REENTRANCE(x) ((*(x))--) 84 1.1 christos 85 1.1 christos #ifdef DEBUG 86 1.1 christos #define Tprintf(...) if (collector_interface) collector_interface->writeDebugInfo( 0, __VA_ARGS__ ) 87 1.1 christos #define TprintfT(...) if (collector_interface) collector_interface->writeDebugInfo( 1, __VA_ARGS__ ) 88 1.1 christos #else 89 1.1 christos #define Tprintf(...) 90 1.1 christos #define TprintfT(...) 91 1.1 christos #endif 92 1.1 christos 93 1.1 christos static void init_module () __attribute__ ((constructor)); 94 1.1 christos 95 1.1 christos static void 96 1.1 christos init_module () 97 1.1 christos { 98 1.1 christos __collector_dlsym_guard = 1; 99 1.1 christos RegModuleFunc reg_module = (RegModuleFunc) dlsym (RTLD_DEFAULT, "__collector_register_module"); 100 1.1 christos __collector_dlsym_guard = 0; 101 1.1 christos if (reg_module == NULL) 102 1.1 christos { 103 1.1 christos TprintfT (0, "clockprof: init_module FAILED -- reg_module = NULL\n"); 104 1.1 christos return; 105 1.1 christos } 106 1.1 christos prof_hndl = reg_module (&module_interface); 107 1.1 christos if (prof_hndl == COLLECTOR_MODULE_ERR && collector_interface != NULL) 108 1.1 christos { 109 1.1 christos Tprintf (0, "clockprof: ERROR: handle not created.\n"); 110 1.1 christos collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">data handle not created</event>\n", SP_JCMD_CERROR, COL_ERROR_PROFINIT); 111 1.1 christos } 112 1.1 christos TprintfT (0, "clockprof: init_module, prof_hndl = %d\n", prof_hndl); 113 1.1 christos return; 114 1.1 christos } 115 1.1 christos 116 1.1 christos static int 117 1.1 christos init_interface (CollectorInterface *_collector_interface) 118 1.1 christos { 119 1.1 christos collector_interface = _collector_interface; 120 1.1 christos return COL_ERROR_NONE; 121 1.1 christos } 122 1.1 christos 123 1.1 christos static int 124 1.1 christos open_experiment (const char *exp) 125 1.1 christos { 126 1.1 christos if (collector_interface == NULL) 127 1.1 christos { 128 1.1 christos Tprintf (0, "clockprof: ERROR: collector_interface is null.\n"); 129 1.1 christos return COL_ERROR_PROFINIT; 130 1.1 christos } 131 1.1 christos const char *params = collector_interface->getParams (); 132 1.1 christos while (params) 133 1.1 christos { 134 1.1 christos if (__collector_strStartWith (params, "p:") == 0) 135 1.1 christos { 136 1.1 christos params += 2; 137 1.1 christos break; 138 1.1 christos } 139 1.1 christos while (*params != 0 && *params != ';') 140 1.1 christos params++; 141 1.1 christos if (*params == 0) 142 1.1 christos params = NULL; 143 1.1 christos else 144 1.1 christos params++; 145 1.1 christos } 146 1.1 christos if (params == NULL) /* Clock profiling not specified */ 147 1.1 christos return COL_ERROR_PROFINIT; 148 1.1 christos TprintfT (0, "clockprof: open_experiment %s -- %s\n", exp, params); 149 1.1 christos int prof_interval = CALL_UTIL (strtol)(params, NULL, 0); 150 1.1 christos prof_key = collector_interface->createKey (sizeof ( int), NULL, NULL); 151 1.1 christos if (prof_key == (unsigned) - 1) 152 1.1 christos { 153 1.1 christos Tprintf (0, "clockprof: ERROR: TSD key create failed.\n"); 154 1.1 christos collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">TSD key not created</event>\n", SP_JCMD_CERROR, COL_ERROR_PROFINIT); 155 1.1 christos return COL_ERROR_PROFINIT; 156 1.1 christos } 157 1.1 christos 158 1.1 christos /* set dispatcher interval timer period used for all timed activities */ 159 1.1 christos int prof_interval_actual = __collector_ext_itimer_set (prof_interval); 160 1.1 christos TprintfT (0, "clockprof: open_experiment(): __collector_ext_itimer_set (actual period=%d, req_period=%d)\n", 161 1.1 christos prof_interval_actual, prof_interval); 162 1.1 christos if (prof_interval_actual <= 0) 163 1.1 christos { 164 1.1 christos collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">itimer could not be set</event>\n", SP_JCMD_CERROR, COL_ERROR_PROFINIT); 165 1.1 christos return COL_ERROR_PROFINIT; 166 1.1 christos } 167 1.1 christos if ((prof_interval_actual >= (prof_interval + prof_interval / 10)) || 168 1.1 christos (prof_interval_actual <= (prof_interval - prof_interval / 10))) 169 1.1 christos collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">%d -> %d</event>\n", SP_JCMD_CWARN, COL_WARN_PROFRND, prof_interval, prof_interval_actual); 170 1.1 christos else if (prof_interval_actual != prof_interval) 171 1.1 christos collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">%d -> %d</event>\n", SP_JCMD_COMMENT, COL_WARN_PROFRND, prof_interval, prof_interval_actual); 172 1.1 christos prof_interval = prof_interval_actual; 173 1.1 christos collector_interface->writeLog ("<profile name=\"%s\" ptimer=\"%d\" numstates=\"%d\">\n", 174 1.1 christos SP_JCMD_PROFILE, prof_interval, LMS_MAGIC_ID_LINUX); 175 1.1 christos collector_interface->writeLog (" <profdata fname=\"%s\"/>\n", 176 1.1 christos module_interface.description); 177 1.1 christos 178 1.1 christos /* Record Profile packet description */ 179 1.1 christos collector_interface->writeLog (" <profpckt kind=\"%d\" uname=\"" STXT ("Clock profiling data") "\">\n", CLOCK_TYPE); 180 1.1 christos collector_interface->writeLog (" <field name=\"LWPID\" uname=\"" STXT ("Lightweight process id") "\" offset=\"%d\" type=\"%s\"/>\n", 181 1.1.1.2 christos (int) offsetof (ClockPacket, lwp_id), 182 1.1.1.2 christos fld_sizeof (ClockPacket, lwp_id) == 4 ? "INT32" : "INT64"); 183 1.1 christos collector_interface->writeLog (" <field name=\"THRID\" uname=\"" STXT ("Thread number") "\" offset=\"%d\" type=\"%s\"/>\n", 184 1.1.1.2 christos (int) offsetof (ClockPacket, thr_id), 185 1.1.1.2 christos fld_sizeof (ClockPacket, thr_id) == 4 ? "INT32" : "INT64"); 186 1.1 christos collector_interface->writeLog (" <field name=\"CPUID\" uname=\"" STXT ("CPU id") "\" offset=\"%d\" type=\"%s\"/>\n", 187 1.1.1.2 christos (int) offsetof (ClockPacket, cpu_id), 188 1.1.1.2 christos fld_sizeof (ClockPacket, cpu_id) == 4 ? "INT32" : "INT64"); 189 1.1 christos collector_interface->writeLog (" <field name=\"TSTAMP\" uname=\"" STXT ("High resolution timestamp") "\" offset=\"%d\" type=\"%s\"/>\n", 190 1.1.1.2 christos (int) offsetof (ClockPacket, tstamp), 191 1.1.1.2 christos fld_sizeof (ClockPacket, tstamp) == 4 ? "INT32" : "INT64"); 192 1.1 christos collector_interface->writeLog (" <field name=\"FRINFO\" offset=\"%d\" type=\"%s\"/>\n", 193 1.1.1.2 christos (int) offsetof (ClockPacket, frinfo), 194 1.1.1.2 christos fld_sizeof (ClockPacket, frinfo) == 4 ? "INT32" : "INT64"); 195 1.1 christos collector_interface->writeLog (" <field name=\"MSTATE\" uname=\"" STXT ("Thread state") "\" offset=\"%d\" type=\"%s\"/>\n", 196 1.1.1.2 christos (int) offsetof (ClockPacket, mstate), 197 1.1.1.2 christos fld_sizeof (ClockPacket, mstate) == 4 ? "INT32" : "INT64"); 198 1.1 christos collector_interface->writeLog (" <field name=\"NTICK\" uname=\"" STXT ("Duration") "\" offset=\"%d\" type=\"%s\"/>\n", 199 1.1.1.2 christos (int) offsetof (ClockPacket, nticks), 200 1.1.1.2 christos fld_sizeof (ClockPacket, nticks) == 4 ? "INT32" : "INT64"); 201 1.1 christos collector_interface->writeLog (" </profpckt>\n"); 202 1.1 christos collector_interface->writeLog ("</profile>\n"); 203 1.1 christos return COL_ERROR_NONE; 204 1.1 christos } 205 1.1 christos 206 1.1 christos static int 207 1.1 christos start_data_collection (void) 208 1.1 christos { 209 1.1 christos TprintfT (0, "clockprof: start_data_collection\n"); 210 1.1 christos prof_mode = 1; 211 1.1 christos return 0; 212 1.1 christos } 213 1.1 christos 214 1.1 christos static int 215 1.1 christos stop_data_collection (void) 216 1.1 christos { 217 1.1 christos prof_mode = 0; 218 1.1 christos TprintfT (0, "clockprof: stop_data_collection\n"); 219 1.1 christos return 0; 220 1.1 christos } 221 1.1 christos 222 1.1 christos static int 223 1.1 christos close_experiment (void) 224 1.1 christos { 225 1.1 christos prof_mode = 0; 226 1.1 christos prof_key = COLLECTOR_TSD_INVALID_KEY; 227 1.1 christos TprintfT (0, "clockprof: close_experiment\n"); 228 1.1 christos return 0; 229 1.1 christos } 230 1.1 christos 231 1.1 christos /* fork child. Clean up state but don't write to experiment */ 232 1.1 christos static int 233 1.1 christos detach_experiment (void) 234 1.1 christos { 235 1.1 christos prof_mode = 0; 236 1.1 christos prof_key = COLLECTOR_TSD_INVALID_KEY; 237 1.1 christos TprintfT (0, "clockprof: detach_experiment\n"); 238 1.1 christos return 0; 239 1.1 christos } 240 1.1 christos 241 1.1 christos /* 242 1.1 christos * void collector_lost_profile_context 243 1.1 christos * Placeholder/marker function used when profiling given NULL context. 244 1.1 christos */ 245 1.1 christos void 246 1.1 christos __collector_lost_profile_context (void) { } 247 1.1 christos 248 1.1 christos /* 249 1.1 christos * void __collector_ext_profile_handler( siginfo_t *info, ucontext_t *context ) 250 1.1 christos * Handle real profile events to collect profile data. 251 1.1 christos */ 252 1.1 christos void 253 1.1 christos __collector_ext_profile_handler (siginfo_t *info, ucontext_t *context) 254 1.1 christos { 255 1.1 christos int *guard; 256 1.1 christos if (!prof_mode) /* sigprof timer running only because hwprofile.c needs it */ 257 1.1 christos return; 258 1.1 christos if (CHCK_REENTRANCE (guard)) 259 1.1 christos { 260 1.1 christos TprintfT (0, "__collector_ext_profile_handler: ERROR: prof_mode=%d guard=%d!\n", 261 1.1 christos prof_mode, guard ? *guard : -2); 262 1.1 christos return; 263 1.1 christos } 264 1.1 christos PUSH_REENTRANCE (guard); 265 1.1.1.3 christos TprintfT (0, "__collector_ext_profile_handler\n"); 266 1.1 christos ucontext_t uctxmem; 267 1.1 christos if (context == NULL) 268 1.1 christos { 269 1.1 christos /* assume this case is rare, and accept overhead of creating dummy_uc */ 270 1.1 christos TprintfT (0, "collector_profile_handler: ERROR: got NULL context!\n"); 271 1.1 christos context = &uctxmem; 272 1.1.1.2 christos CALL_UTIL (getcontext) (context); /* initialize dummy context */ 273 1.1 christos SETFUNCTIONCONTEXT (context, &__collector_lost_profile_context); 274 1.1 christos } 275 1.1.1.3 christos static ClockPacket clock_pckt_0 = {.comm.type = CLOCK_TYPE, 276 1.1.1.3 christos .comm.tsize = sizeof (ClockPacket)}; 277 1.1.1.3 christos ClockPacket pckt = clock_pckt_0; 278 1.1 christos pckt.lwp_id = __collector_lwp_self (); 279 1.1 christos pckt.thr_id = __collector_thr_self (); 280 1.1 christos pckt.cpu_id = CALL_UTIL (getcpuid)(); 281 1.1 christos pckt.tstamp = collector_interface->getHiResTime (); 282 1.1 christos pckt.frinfo = collector_interface->getFrameInfo (COLLECTOR_MODULE_ERR, pckt.tstamp, FRINFO_FROM_UC, context); 283 1.1 christos pckt.mstate = LMS_LINUX_CPU; 284 1.1 christos pckt.nticks = 1; 285 1.1 christos collector_interface->writeDataPacket (prof_hndl, (CM_Packet*) & pckt); 286 1.1 christos POP_REENTRANCE (guard); 287 1.1 christos } 288