1 /* Copyright (C) 2021-2023 Free Software Foundation, Inc.
4 This file is part of GNU Binutils.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3, or (at your option)
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, 51 Franklin Street - Fifth Floor, Boston,
19 MA 02110-1301, USA. */
24 * Note: SIGPROF signal-handling and interval timer (once exclusive to
25 * profile handling) are now common services provided by the dispatcher.
36 #include "collector_module.h"
37 #include "gp-experiment.h"
38 #include "data_pckts.h"
39 #include "libcol_util.h"
40 #include "hwprofile.h"
43 /* TprintfT(<level>,...) definitions. Adjust per module as needed */
44 #define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings
45 #define DBG_LT1 1 // for configuration details, warnings
49 static int init_interface (CollectorInterface
*);
50 static int open_experiment (const char *);
51 static int start_data_collection (void);
52 static int stop_data_collection (void);
53 static int close_experiment (void);
54 static int detach_experiment (void);
56 static ModuleInterface module_interface
={
57 SP_PROFILE_FILE
, /* description */
58 init_interface
, /* initInterface */
59 open_experiment
, /* openExperiment */
60 start_data_collection
, /* startDataCollection */
61 stop_data_collection
, /* stopDataCollection */
62 close_experiment
, /* closeExperiment */
63 detach_experiment
/* detachExperiment (fork child) */
66 static CollectorInterface
*collector_interface
= NULL
;
67 static int prof_mode
= 0;
68 static CollectorModule prof_hndl
= COLLECTOR_MODULE_ERR
;
69 static unsigned prof_key
= COLLECTOR_TSD_INVALID_KEY
;
71 typedef struct ClockPacket
72 { /* clock profiling packet */
77 hrtime_t tstamp
__attribute__ ((packed
));
78 uint64_t frinfo
__attribute__ ((packed
));
79 int mstate
; /* kernel microstate */
80 int nticks
; /* number of ticks in that state */
83 /* XXX should be able to use local types */
84 #define CLOCK_TYPE OPROF_PCKT
86 #define CHCK_REENTRANCE(x) ( !prof_mode || ((x) = collector_interface->getKey( prof_key )) == NULL || (*(x) != 0) )
87 #define PUSH_REENTRANCE(x) ((*(x))++)
88 #define POP_REENTRANCE(x) ((*(x))--)
91 #define Tprintf(...) if (collector_interface) collector_interface->writeDebugInfo( 0, __VA_ARGS__ )
92 #define TprintfT(...) if (collector_interface) collector_interface->writeDebugInfo( 1, __VA_ARGS__ )
98 static void init_module () __attribute__ ((constructor
));
103 __collector_dlsym_guard
= 1;
104 RegModuleFunc reg_module
= (RegModuleFunc
) dlsym (RTLD_DEFAULT
, "__collector_register_module");
105 __collector_dlsym_guard
= 0;
106 if (reg_module
== NULL
)
108 TprintfT (0, "clockprof: init_module FAILED -- reg_module = NULL\n");
111 prof_hndl
= reg_module (&module_interface
);
112 if (prof_hndl
== COLLECTOR_MODULE_ERR
&& collector_interface
!= NULL
)
114 Tprintf (0, "clockprof: ERROR: handle not created.\n");
115 collector_interface
->writeLog ("<event kind=\"%s\" id=\"%d\">data handle not created</event>\n", SP_JCMD_CERROR
, COL_ERROR_PROFINIT
);
117 TprintfT (0, "clockprof: init_module, prof_hndl = %d\n", prof_hndl
);
122 init_interface (CollectorInterface
*_collector_interface
)
124 collector_interface
= _collector_interface
;
125 return COL_ERROR_NONE
;
129 open_experiment (const char *exp
)
131 if (collector_interface
== NULL
)
133 Tprintf (0, "clockprof: ERROR: collector_interface is null.\n");
134 return COL_ERROR_PROFINIT
;
136 const char *params
= collector_interface
->getParams ();
139 if (__collector_strStartWith (params
, "p:") == 0)
144 while (*params
!= 0 && *params
!= ';')
151 if (params
== NULL
) /* Clock profiling not specified */
152 return COL_ERROR_PROFINIT
;
153 TprintfT (0, "clockprof: open_experiment %s -- %s\n", exp
, params
);
154 int prof_interval
= CALL_UTIL (strtol
)(params
, NULL
, 0);
155 prof_key
= collector_interface
->createKey (sizeof ( int), NULL
, NULL
);
156 if (prof_key
== (unsigned) - 1)
158 Tprintf (0, "clockprof: ERROR: TSD key create failed.\n");
159 collector_interface
->writeLog ("<event kind=\"%s\" id=\"%d\">TSD key not created</event>\n", SP_JCMD_CERROR
, COL_ERROR_PROFINIT
);
160 return COL_ERROR_PROFINIT
;
163 /* set dispatcher interval timer period used for all timed activities */
164 int prof_interval_actual
= __collector_ext_itimer_set (prof_interval
);
165 TprintfT (0, "clockprof: open_experiment(): __collector_ext_itimer_set (actual period=%d, req_period=%d)\n",
166 prof_interval_actual
, prof_interval
);
167 if (prof_interval_actual
<= 0)
169 collector_interface
->writeLog ("<event kind=\"%s\" id=\"%d\">itimer could not be set</event>\n", SP_JCMD_CERROR
, COL_ERROR_PROFINIT
);
170 return COL_ERROR_PROFINIT
;
172 if ((prof_interval_actual
>= (prof_interval
+ prof_interval
/ 10)) ||
173 (prof_interval_actual
<= (prof_interval
- prof_interval
/ 10)))
174 collector_interface
->writeLog ("<event kind=\"%s\" id=\"%d\">%d -> %d</event>\n", SP_JCMD_CWARN
, COL_WARN_PROFRND
, prof_interval
, prof_interval_actual
);
175 else if (prof_interval_actual
!= prof_interval
)
176 collector_interface
->writeLog ("<event kind=\"%s\" id=\"%d\">%d -> %d</event>\n", SP_JCMD_COMMENT
, COL_WARN_PROFRND
, prof_interval
, prof_interval_actual
);
177 prof_interval
= prof_interval_actual
;
178 collector_interface
->writeLog ("<profile name=\"%s\" ptimer=\"%d\" numstates=\"%d\">\n",
179 SP_JCMD_PROFILE
, prof_interval
, LMS_MAGIC_ID_LINUX
);
180 collector_interface
->writeLog (" <profdata fname=\"%s\"/>\n",
181 module_interface
.description
);
183 /* Record Profile packet description */
184 ClockPacket
*cp
= NULL
;
185 collector_interface
->writeLog (" <profpckt kind=\"%d\" uname=\"" STXT ("Clock profiling data") "\">\n", CLOCK_TYPE
);
186 collector_interface
->writeLog (" <field name=\"LWPID\" uname=\"" STXT ("Lightweight process id") "\" offset=\"%d\" type=\"%s\"/>\n",
187 &cp
->lwp_id
, sizeof (cp
->lwp_id
) == 4 ? "INT32" : "INT64");
188 collector_interface
->writeLog (" <field name=\"THRID\" uname=\"" STXT ("Thread number") "\" offset=\"%d\" type=\"%s\"/>\n",
189 &cp
->thr_id
, sizeof (cp
->thr_id
) == 4 ? "INT32" : "INT64");
190 collector_interface
->writeLog (" <field name=\"CPUID\" uname=\"" STXT ("CPU id") "\" offset=\"%d\" type=\"%s\"/>\n",
191 &cp
->cpu_id
, sizeof (cp
->cpu_id
) == 4 ? "INT32" : "INT64");
192 collector_interface
->writeLog (" <field name=\"TSTAMP\" uname=\"" STXT ("High resolution timestamp") "\" offset=\"%d\" type=\"%s\"/>\n",
193 &cp
->tstamp
, sizeof (cp
->tstamp
) == 4 ? "INT32" : "INT64");
194 collector_interface
->writeLog (" <field name=\"FRINFO\" offset=\"%d\" type=\"%s\"/>\n",
195 &cp
->frinfo
, sizeof (cp
->frinfo
) == 4 ? "INT32" : "INT64");
196 collector_interface
->writeLog (" <field name=\"MSTATE\" uname=\"" STXT ("Thread state") "\" offset=\"%d\" type=\"%s\"/>\n",
197 &cp
->mstate
, sizeof (cp
->mstate
) == 4 ? "INT32" : "INT64");
198 collector_interface
->writeLog (" <field name=\"NTICK\" uname=\"" STXT ("Duration") "\" offset=\"%d\" type=\"%s\"/>\n",
199 &cp
->nticks
, sizeof (cp
->nticks
) == 4 ? "INT32" : "INT64");
200 collector_interface
->writeLog (" </profpckt>\n");
201 collector_interface
->writeLog ("</profile>\n");
202 return COL_ERROR_NONE
;
206 start_data_collection (void)
208 TprintfT (0, "clockprof: start_data_collection\n");
214 stop_data_collection (void)
217 TprintfT (0, "clockprof: stop_data_collection\n");
222 close_experiment (void)
225 prof_key
= COLLECTOR_TSD_INVALID_KEY
;
226 TprintfT (0, "clockprof: close_experiment\n");
230 /* fork child. Clean up state but don't write to experiment */
232 detach_experiment (void)
235 prof_key
= COLLECTOR_TSD_INVALID_KEY
;
236 TprintfT (0, "clockprof: detach_experiment\n");
241 * void collector_lost_profile_context
242 * Placeholder/marker function used when profiling given NULL context.
245 __collector_lost_profile_context (void) { }
248 * void __collector_ext_profile_handler( siginfo_t *info, ucontext_t *context )
249 * Handle real profile events to collect profile data.
252 __collector_ext_profile_handler (siginfo_t
*info
, ucontext_t
*context
)
255 if (!prof_mode
) /* sigprof timer running only because hwprofile.c needs it */
257 if (CHCK_REENTRANCE (guard
))
259 TprintfT (0, "__collector_ext_profile_handler: ERROR: prof_mode=%d guard=%d!\n",
260 prof_mode
, guard
? *guard
: -2);
263 PUSH_REENTRANCE (guard
);
264 TprintfT (DBG_LT3
, "__collector_ext_profile_handler\n");
268 /* assume this case is rare, and accept overhead of creating dummy_uc */
269 TprintfT (0, "collector_profile_handler: ERROR: got NULL context!\n");
271 CALL_UTIL (getcontext
) (context
); /* initialize dummy context */
272 SETFUNCTIONCONTEXT (context
, &__collector_lost_profile_context
);
275 CALL_UTIL (memset
)(&pckt
, 0, sizeof ( pckt
));
276 pckt
.comm
.tsize
= sizeof ( pckt
);
277 pckt
.comm
.type
= CLOCK_TYPE
;
278 pckt
.lwp_id
= __collector_lwp_self ();
279 pckt
.thr_id
= __collector_thr_self ();
280 pckt
.cpu_id
= CALL_UTIL (getcpuid
)();
281 pckt
.tstamp
= collector_interface
->getHiResTime ();
282 pckt
.frinfo
= collector_interface
->getFrameInfo (COLLECTOR_MODULE_ERR
, pckt
.tstamp
, FRINFO_FROM_UC
, context
);
283 pckt
.mstate
= LMS_LINUX_CPU
;
285 collector_interface
->writeDataPacket (prof_hndl
, (CM_Packet
*) & pckt
);
286 POP_REENTRANCE (guard
);