1 /*
2 Copyright (c) 2009-2018, Intel Corporation
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6
7 * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
8 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the dis
9 tribution.
10 * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
11
12 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNES
13 S FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDI
14 NG, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRI
15 CT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16 */
17 // written by Andrey Semin and many others
18
19 #include <iostream>
20 #include <cassert>
21 #include <climits>
22 #ifdef _MSC_VER
23 #include <process.h>
24 #include <comdef.h>
25 #else
26 #include <sys/wait.h> // for waitpid()
27 #include <unistd.h> // for ::sleep
28 #endif
29 #include "utils.h"
30 #include "cpucounters.h"
31
32 namespace pcm {
33
34 void (*post_cleanup_callback)(void) = NULL;
35
36 //! \brief handler of exit() call
exit_cleanup(void)37 void exit_cleanup(void)
38 {
39 std::cout << std::flush;
40
41 restore_signal_handlers();
42
43 // this replaces same call in cleanup() from util.h
44 PCM::getInstance()->cleanup(); // this replaces same call in cleanup() from util.h
45
46 //TODO: delete other shared objects.... if any.
47
48 if(post_cleanup_callback != NULL)
49 {
50 post_cleanup_callback();
51 }
52 }
53
print_cpu_details()54 void print_cpu_details()
55 {
56 const auto m = PCM::getInstance();
57 std::cerr << "\nDetected " << m->getCPUBrandString() << " \"Intel(r) microarchitecture codename " <<
58 m->getUArchCodename() << "\" stepping " << m->getCPUStepping();
59 const auto ucode_level = m->getCPUMicrocodeLevel();
60 if (ucode_level >= 0)
61 {
62 std::cerr << " microcode level 0x" << std::hex << ucode_level;
63 }
64 std::cerr << "\n";
65 }
66
67 #ifdef _MSC_VER
68
ThreadGroupTempAffinity(uint32 core_id,bool checkStatus)69 ThreadGroupTempAffinity::ThreadGroupTempAffinity(uint32 core_id, bool checkStatus)
70 {
71 GROUP_AFFINITY NewGroupAffinity;
72 memset(&NewGroupAffinity, 0, sizeof(GROUP_AFFINITY));
73 memset(&PreviousGroupAffinity, 0, sizeof(GROUP_AFFINITY));
74 DWORD currentGroupSize = 0;
75
76 while ((DWORD)core_id >= (currentGroupSize = GetActiveProcessorCount(NewGroupAffinity.Group)))
77 {
78 core_id -= (uint32)currentGroupSize;
79 ++NewGroupAffinity.Group;
80 }
81 NewGroupAffinity.Mask = 1ULL << core_id;
82 const auto res = SetThreadGroupAffinity(GetCurrentThread(), &NewGroupAffinity, &PreviousGroupAffinity);
83 if (res == FALSE && checkStatus)
84 {
85 std::cerr << "ERROR: SetThreadGroupAffinity for core " << core_id << " failed with error " << GetLastError() << "\n";
86 throw std::exception();
87 }
88 }
~ThreadGroupTempAffinity()89 ThreadGroupTempAffinity::~ThreadGroupTempAffinity()
90 {
91 SetThreadGroupAffinity(GetCurrentThread(), &PreviousGroupAffinity, NULL);
92 }
93
unhandled_exception_handler(LPEXCEPTION_POINTERS p)94 LONG unhandled_exception_handler(LPEXCEPTION_POINTERS p)
95 {
96 std::cerr << "DEBUG: Unhandled Exception event\n";
97 exit(EXIT_FAILURE);
98 }
99
100 /**
101 * \brief version of interrupt handled for Windows
102 */
sigINT_handler(DWORD fdwCtrlType)103 BOOL sigINT_handler(DWORD fdwCtrlType)
104 {
105 // output for DEBUG only
106 std::cerr << "DEBUG: caught signal to interrupt: ";
107 switch (fdwCtrlType)
108 {
109 // Handle the CTRL-C signal.
110 case CTRL_C_EVENT:
111 std::cerr << "Ctrl-C event\n";
112 break;
113
114 // CTRL-CLOSE: confirm that the user wants to exit.
115 case CTRL_CLOSE_EVENT:
116 std::cerr << "Ctrl-Close event\n";
117 break;
118
119 // Pass other signals to the next handler.
120 case CTRL_BREAK_EVENT:
121 std::cerr << "Ctrl-Break event\n";
122 break;
123
124 case CTRL_LOGOFF_EVENT:
125 std::cerr << "Ctrl-Logoff event\n";
126 break;
127
128 case CTRL_SHUTDOWN_EVENT:
129 std::cerr << "Ctrl-Shutdown event\n";
130 break;
131
132 default:
133 std::cerr << "Unknown event\n";
134 break;
135 }
136
137 // TODO: dump summary, if needed
138
139 // in case PCM is blocked just return and summary will be dumped in
140 // calling function, if needed
141 if (PCM::getInstance()->isBlocked()) {
142 return FALSE;
143 } else {
144 exit_cleanup();
145 _exit(EXIT_SUCCESS);
146 return FALSE; // to prevent Warning
147 }
148 }
149
150 /**
151 * \brief started in a separate thread and blocks waiting for child applicaiton to exit.
152 * After child app exits: -> print Child's termination status and terminates PCM
153 */
waitForChild(void * proc_id)154 void waitForChild(void * proc_id)
155 {
156 intptr_t procHandle = (intptr_t)proc_id;
157 int termstat;
158 _cwait(&termstat, procHandle, _WAIT_CHILD);
159 std::cerr << "Program exited with status " << termstat << "\n";
160 exit(EXIT_SUCCESS);
161 }
162 #else
163 /**
164 * \brief handles signals that lead to termination of the program
165 * such as SIGINT, SIGQUIT, SIGABRT, SIGSEGV, SIGTERM, SIGCHLD
166 * this function specifically works when the client aplication launched
167 * by pcm -- terminates
168 */
sigINT_handler(int signum)169 void sigINT_handler(int signum)
170 {
171 // output for DEBUG only
172 std::cerr << "DEBUG: caught signal to interrupt (" << strsignal(signum) << ").\n";
173 // TODO: dump summary, if needed
174
175 // in case PCM is blocked just return and summary will be dumped in
176 // calling function, if needed
177 if (PCM::getInstance()->isBlocked()) {
178 return;
179 } else {
180 exit_cleanup();
181 _exit(EXIT_SUCCESS);
182 }
183 }
184
185 /**
186 * \brief handles signals that lead to restart the application
187 * such as SIGHUP.
188 * for example to re-read environment variables controlling PCM execution
189 */
sigHUP_handler(int)190 void sigHUP_handler(int /*signum*/)
191 {
192 // output for DEBUG only
193 std::cerr << "DEBUG: caught signal to hangup. Reloading configuration and continue...\n";
194 // TODO: restart; so far do nothing
195
196 return; // continue program execution
197 }
198
199 /**
200 * \brief handles signals that lead to update of configuration
201 * such as SIGUSR1 and SIGUSR2.
202 * for the future extensions
203 */
sigUSR_handler(int)204 void sigUSR_handler(int /*signum*/)
205 {
206 std::cerr << "DEBUG: caught USR signal. Continue.\n";
207 // TODO: reload configurationa, reset accumulative counters;
208
209 return;
210 }
211
212 /**
213 * \brief handles signals that lead to update of configuration
214 * such as SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU
215 */
sigSTOP_handler(int)216 void sigSTOP_handler(int /*signum*/)
217 {
218 PCM * m = PCM::getInstance();
219 int runState = m->getRunState();
220 std::string state = (runState == 1 ? "suspend" : "continue");
221 std::cerr << "DEBUG: caught signal to " << state << " execution.\n"; // debug of signals only
222 if (runState == 1) {
223 // stop counters and sleep... almost forever;
224 m->setRunState(0);
225 sleep(INT_MAX);
226 } else {
227 // resume
228 m->setRunState(1);
229 alarm(1);
230 }
231 return;
232 }
233
234 /**
235 * \brief handles signals that lead to update of configuration
236 * such as SIGCONT
237 */
sigCONT_handler(int)238 void sigCONT_handler(int /*signum*/)
239 {
240 std::cout << "DEBUG: caught signal to continue execution.\n"; // debug of signals only
241 // TODO: clear counters, resume counting.
242 return;
243 }
244 #endif // ifdef _MSC_VER
245
246 //! \brief install various handlers for system signals
set_signal_handlers(void)247 void set_signal_handlers(void)
248 {
249 if (atexit(exit_cleanup) != 0)
250 {
251 std::cerr << "ERROR: Failed to install exit handler.\n";
252 return;
253 }
254
255 #ifdef _MSC_VER
256 BOOL handlerStatus;
257 // Increase the priority a bit to improve context switching delays on Windows
258 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
259 // to fix Cygwin/BASH setting Ctrl+C handler need first to restore the default one
260 handlerStatus = SetConsoleCtrlHandler(NULL, FALSE); // restores normal processing of CTRL+C input
261 if (handlerStatus == 0) {
262 std::wcerr << "Failed to set Ctrl+C hanlder. Error code: " << GetLastError() << " ";
263 const TCHAR * errorStr = _com_error(GetLastError()).ErrorMessage();
264 if (errorStr) std::wcerr << errorStr;
265 std::wcerr << "\n";
266 _exit(EXIT_FAILURE);
267 }
268 handlerStatus = SetConsoleCtrlHandler((PHANDLER_ROUTINE)sigINT_handler, TRUE);
269 if (handlerStatus == 0) {
270 std::wcerr << "Failed to set Ctrl+C hanlder. Error code: " << GetLastError() << " ";
271 const TCHAR * errorStr = _com_error(GetLastError()).ErrorMessage();
272 if (errorStr) std::wcerr << errorStr;
273 std::wcerr << "\n";
274 _exit(EXIT_FAILURE);
275 }
276 SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)&unhandled_exception_handler);
277 char *envPath;
278 if (_dupenv_s(&envPath, NULL, "_"))
279 {
280 std::cerr << "\nPCM ERROR: _dupenv_s failed.\n";
281 _exit(EXIT_FAILURE);
282 }
283 free(envPath);
284 if (envPath)
285 {
286 std::cerr << "\nPCM ERROR: Detected cygwin/mingw environment which does not allow to setup PMU clean-up handlers on Ctrl-C and other termination signals.\n";
287 std::cerr << "See https://www.mail-archive.com/cygwin@cygwin.com/msg74817.html\n";
288 std::cerr << "As a workaround please run pcm directly from a native windows shell (e.g. cmd).\n";
289 std::cerr << "Exiting...\n\n";
290 _exit(EXIT_FAILURE);
291 }
292 std::cerr << "DEBUG: Setting Ctrl+C done.\n";
293
294 #else
295 struct sigaction saINT, saHUP, saUSR, saSTOP, saCONT;
296
297 // install handlers that interrupt execution
298 saINT.sa_handler = sigINT_handler;
299 sigemptyset(&saINT.sa_mask);
300 saINT.sa_flags = SA_RESTART;
301 sigaction(SIGINT, &saINT, NULL);
302 sigaction(SIGQUIT, &saINT, NULL);
303 sigaction(SIGABRT, &saINT, NULL);
304 sigaction(SIGTERM, &saINT, NULL);
305 sigaction(SIGSEGV, &saINT, NULL);
306
307 saINT.sa_flags = SA_RESTART | SA_NOCLDSTOP;
308 sigaction(SIGCHLD, &saINT, NULL); // get there is our child exits. do nothing if it stoped/continued
309
310 // install SIGHUP handler to restart
311 saHUP.sa_handler = sigHUP_handler;
312 sigemptyset(&saHUP.sa_mask);
313 saHUP.sa_flags = SA_RESTART;
314 sigaction(SIGHUP, &saHUP, NULL);
315
316 // install SIGHUP handler to restart
317 saUSR.sa_handler = sigUSR_handler;
318 sigemptyset(&saUSR.sa_mask);
319 saUSR.sa_flags = SA_RESTART;
320 sigaction(SIGUSR1, &saUSR, NULL);
321 sigaction(SIGUSR2, &saUSR, NULL);
322
323 // install SIGSTOP handler: pause/resume
324 saSTOP.sa_handler = sigSTOP_handler;
325 sigemptyset(&saSTOP.sa_mask);
326 saSTOP.sa_flags = SA_RESTART;
327 sigaction(SIGSTOP, &saSTOP, NULL);
328 sigaction(SIGTSTP, &saSTOP, NULL);
329 sigaction(SIGTTIN, &saSTOP, NULL);
330 sigaction(SIGTTOU, &saSTOP, NULL);
331
332 // install SIGCONT & SIGALRM handler
333 saCONT.sa_handler = sigCONT_handler;
334 sigemptyset(&saCONT.sa_mask);
335 saCONT.sa_flags = SA_RESTART;
336 sigaction(SIGCONT, &saCONT, NULL);
337 sigaction(SIGALRM, &saCONT, NULL);
338
339 #endif
340 return;
341 }
342
343 //! \brief Restores default signal handlers under Linux/UNIX
restore_signal_handlers(void)344 void restore_signal_handlers(void)
345 {
346 #ifndef _MSC_VER
347 struct sigaction action;
348 action.sa_handler = SIG_DFL;
349
350 sigaction(SIGINT, &action, NULL);
351 sigaction(SIGQUIT, &action, NULL);
352 sigaction(SIGABRT, &action, NULL);
353 sigaction(SIGTERM, &action, NULL);
354 sigaction(SIGSEGV, &action, NULL);
355
356 sigaction(SIGCHLD, &action, NULL);
357
358 // restore SIGHUP handler to restart
359 sigaction(SIGHUP, &action, NULL);
360
361 // restore SIGHUP handler to restart
362 sigaction(SIGUSR1, &action, NULL);
363 sigaction(SIGUSR2, &action, NULL);
364
365 // restore SIGSTOP handler: pause/resume
366 // sigaction(SIGSTOP, &action, NULL); // cannot catch this
367 // handle SUSP character: normally C-z)
368 sigaction(SIGTSTP, &action, NULL);
369 sigaction(SIGTTIN, &action, NULL);
370 sigaction(SIGTTOU, &action, NULL);
371
372 // restore SIGCONT & SIGALRM handler
373 sigaction(SIGCONT, &action, NULL);
374 sigaction(SIGALRM, &action, NULL);
375 #endif
376
377 return;
378 }
379
set_post_cleanup_callback(void (* cb)(void))380 void set_post_cleanup_callback(void(*cb)(void))
381 {
382 post_cleanup_callback = cb;
383 }
384
385 //!\brief launches external program in a separate process
MySystem(char * sysCmd,char ** sysArgv)386 void MySystem(char * sysCmd, char ** sysArgv)
387 {
388 if (sysCmd == NULL) {
389 assert("No program provided. NULL pointer");
390 exit(EXIT_FAILURE);
391 }
392 std::cerr << "\nExecuting \"";
393 std::cerr << sysCmd;
394 std::cerr << "\" command:\n";
395 #ifdef _MSC_VER
396 intptr_t ret;
397 char cbuf[128];
398
399 if (PCM::getInstance()->isBlocked()) { // synchronous start: wait for child process completion
400 // in case PCM should be blocked waiting for the child applicaiton to end
401 // 1. returns and ret = -1 in case of error creating process is encountered
402 // 2.
403 ret = _spawnvp(_P_WAIT, sysCmd, sysArgv);
404 if (ret == -1) { // process creation failed.
405 strerror_s(cbuf, 128, errno);
406 std::cerr << "Failed to start program \"" << sysCmd << "\". " << cbuf << "\n";
407 exit(EXIT_FAILURE);
408 } else { // process created, worked, and completed with exist code in ret. ret=0 -> Success
409 std::cerr << "Program exited with status " << ret << "\n";
410 }
411 } else { // async start: PCM works in parallel with the child process, and exits when
412 ret = _spawnvp(_P_NOWAIT, sysCmd, sysArgv);
413 if (ret == -1) {
414 strerror_s(cbuf, 128, errno);
415 std::cerr << "Failed to start program \"" << sysCmd << "\". " << cbuf << "\n";
416 exit(EXIT_FAILURE);
417 } else { // ret here is the new process handle.
418 // start new thread which will wait for child completion, and continue PCM's execution
419 if (_beginthread(waitForChild, 0, (void *)ret) == -1L) {
420 strerror_s(cbuf, 128, errno);
421 std::cerr << "WARNING: Failed to set waitForChild. PCM will countinue infinitely: finish it manually! " << cbuf << "\n";
422 }
423 }
424 }
425 #else
426 pid_t child_pid = fork();
427
428 if (child_pid == 0) {
429 execvp(sysCmd, sysArgv);
430 std::cerr << "Failed to start program \"" << sysCmd << "\"\n";
431 exit(EXIT_FAILURE);
432 }
433 else
434 {
435 if (PCM::getInstance()->isBlocked()) {
436 int res;
437 waitpid(child_pid, &res, 0);
438 std::cerr << "Program " << sysCmd << " launched with PID: " << child_pid << "\n";
439
440 if (WIFEXITED(res)) {
441 std::cerr << "Program exited with status " << WEXITSTATUS(res) << "\n";
442 }
443 else if (WIFSIGNALED(res)) {
444 std::cerr << "Process " << child_pid << " was terminated with status " << WTERMSIG(res);
445 }
446 }
447 }
448 #endif
449 }
450
451 #ifdef _MSC_VER
452 #define HORIZONTAL char(196)
453 #define VERTICAL char(179)
454 #define DOWN_AND_RIGHT char(218)
455 #define DOWN_AND_LEFT char(191)
456 #define UP_AND_RIGHT char(192)
457 #define UP_AND_LEFT char(217)
458 #else
459 #define HORIZONTAL u8"\u2500"
460 #define VERTICAL u8"\u2502"
461 #define DOWN_AND_RIGHT u8"\u250C"
462 #define DOWN_AND_LEFT u8"\u2510"
463 #define UP_AND_RIGHT u8"\u2514"
464 #define UP_AND_LEFT u8"\u2518"
465 #endif
466
467 template <class T>
drawBar(const int nempty,const T & first,const int width,const T & last)468 void drawBar(const int nempty, const T & first, const int width, const T & last)
469 {
470 for (int c = 0; c < nempty; ++c)
471 {
472 std::cout << ' ';
473 }
474 std::cout << first;
475 for (int c = 0; c < width; ++c)
476 {
477 std::cout << HORIZONTAL;
478 }
479 std::cout << last << '\n';
480 }
481
drawStackedBar(const std::string & label,std::vector<StackedBarItem> & h,const int width)482 void drawStackedBar(const std::string & label, std::vector<StackedBarItem> & h, const int width)
483 {
484 int real_width = 0;
485 auto scale = [&width](double fraction)
486 {
487 return int(round(fraction * double(width)));
488 };
489 for (const auto & i : h)
490 {
491 real_width += scale(i.fraction);
492 }
493 if (real_width > 2*width)
494 {
495 std::cout << "ERROR: sum of fractions > 2 ("<< real_width << " > " << width << ")\n";
496 return;
497 }
498 drawBar((int)label.length(), DOWN_AND_RIGHT, real_width, DOWN_AND_LEFT);
499 std::cout << label << VERTICAL;
500 for (const auto & i : h)
501 {
502 const int c_width = scale(i.fraction);
503 for (int c = 0; c < c_width; ++c)
504 {
505 std::cout << i.fill;
506 }
507 }
508 std::cout << VERTICAL << "\n";
509 drawBar((int)label.length(), UP_AND_RIGHT, real_width, UP_AND_LEFT);
510 }
511
512
CheckAndForceRTMAbortMode(const char * arg,PCM * m)513 bool CheckAndForceRTMAbortMode(const char * arg, PCM * m)
514 {
515 if (strncmp(arg, "-force-rtm-abort-mode", 21) == 0)
516 {
517 m->enableForceRTMAbortMode();
518 return true;
519 }
520 return false;
521 }
522
split(const std::string & str,const char delim)523 std::vector<std::string> split(const std::string & str, const char delim)
524 {
525 std::string token;
526 std::vector<std::string> result;
527 std::istringstream strstr(str);
528 while (std::getline(strstr, token, delim))
529 {
530 result.push_back(token);
531 }
532 return result;
533 }
534
read_number(const char * str)535 uint64 read_number(const char* str)
536 {
537 std::istringstream stream(str);
538 if (strstr(str, "x")) stream >> std::hex;
539 uint64 result = 0;
540 stream >> result;
541 return result;
542 }
543
544 // emulates scanf %i for hex 0x prefix otherwise assumes dec (no oct support)
match(const std::string & subtoken,const std::string & sname,uint64 * result)545 bool match(const std::string& subtoken, const std::string& sname, uint64* result)
546 {
547 if (pcm_sscanf(subtoken) >> s_expect(sname + "0x") >> std::hex >> *result)
548 return true;
549
550 if (pcm_sscanf(subtoken) >> s_expect(sname) >> std::dec >> *result)
551 return true;
552
553 return false;
554 }
555
556 #define PCM_CALIBRATION_INTERVAL 50 // calibrate clock only every 50th iteration
557
calibratedSleep(const double delay,const char * sysCmd,const MainLoop & mainLoop,PCM * m)558 int calibratedSleep(const double delay, const char* sysCmd, const MainLoop& mainLoop, PCM* m)
559 {
560 static uint64 TimeAfterSleep = 0;
561 int delay_ms = int(delay * 1000);
562
563 if (TimeAfterSleep) delay_ms -= (int)(m->getTickCount() - TimeAfterSleep);
564 if (delay_ms < 0) delay_ms = 0;
565
566 if (sysCmd == NULL || mainLoop.getNumberOfIterations() != 0 || m->isBlocked() == false)
567 {
568 if (delay_ms > 0)
569 {
570 // std::cerr << "DEBUG: sleeping for " << std::dec << delay_ms << " ms...\n";
571 MySleepMs(delay_ms);
572 }
573 }
574
575 TimeAfterSleep = m->getTickCount();
576
577 return delay_ms;
578 };
579
print_help_force_rtm_abort_mode(const int alignment)580 void print_help_force_rtm_abort_mode(const int alignment)
581 {
582 const auto m = PCM::getInstance();
583 if (m->isForceRTMAbortModeAvailable() && (m->getMaxCustomCoreEvents() < 4))
584 {
585 std::cerr << " -force-rtm-abort-mode";
586 for (int i = 0; i < (alignment - 23); ++i)
587 {
588 std::cerr << " ";
589 }
590 std::cerr << "=> force RTM transaction abort mode to enable more programmable counters\n";
591 }
592 }
593
594 } // namespace pcm