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