1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
14 Implementation of PAL Performance trace utilities.
28 #include "perftrace.h"
30 /* Standard Headers */
36 #define snprintf _snprintf
37 #define MiscGetenv getenv
38 #define pthread_getspecific TlsGetValue
39 #define THREADSilentGetCurrentThreadId GetCurrentThreadId
40 #define getpid GetCurrentProcessId
41 #define PAL_fgets fgets // on Windows, we want fgets.
42 #define PAL_fwrite fwrite // on Windows, we want fwrite.
43 #define PAL_fseek fseek // on Windows, we want fseek.
47 #include "pal/palinternal.h"
48 #include "pal/perftrace.h"
49 #include "pal/dbgmsg.h"
50 #include "pal/cruntime.h"
53 /* Standard headers */
58 #include <pthread.h> /* for pthread_self */
62 SET_DEFAULT_DEBUG_CHANNEL(MISC);
63 #endif //End of PLATFORM_UNIX
66 #define PAL_PERF_MAX_LOGLINE 0x400 /* 1K */
67 #define PAL_PERF_MAX_INPUT 0x1000 /* 4k for single line of input file */
68 #define PAL_PERF_MAX_FUNCTION_NAME 128 /* any one want a function name longer than 127 bytes? */
69 #define PAL_PERF_PROFILE_BUFFER_SIZE 0x400000 /* 4M */
70 #define PAL_PERF_BUFFER_FULL (PAL_PERF_PROFILE_BUFFER_SIZE - PAL_PERF_MAX_LOGLINE ) /* (Buffer size - 1K) */
72 typedef struct _pal_perf_api_info
74 ULONGLONG entries; /* number of PERF_ENTRY calls for an API function */
75 ULONGLONG counter; /* number of PERF_EXIT calls for an API function */
76 ULONGLONG min_duration; /* Minimum duration in CPU clock ticks in an API function */
77 ULONGLONG max_duration; /* Maximum duration in CPU clock ticks in an API function */
78 ULONGLONG sum_duration; /* Sum of duration*/
79 double sum_of_square_duration; /* Sum of square of durations */
80 DWORD *histograms; /* An array to store the histogram of an API execution cpu ticks. */
84 typedef struct _pal_perf_thread_info
87 pal_perf_api_info * api_table;
91 ULONGLONG start_ticks;
92 ULONGLONG total_duration;
93 } pal_perf_thread_info;
95 typedef struct _pal_thread_list_node
97 pal_perf_thread_info * thread_info;
98 struct _pal_thread_list_node * next;
100 } pal_thread_list_node;
102 typedef struct _pal_perf_program_info
104 char command_line[PAL_PERF_MAX_LOGLINE];
105 char exe_path[PAL_PERF_MAX_LOGLINE];
106 char hostname[PAL_PERF_MAX_FUNCTION_NAME];
107 double cpu_clock_frequency;
108 ULONGLONG start_ticks;
109 ULONGLONG elapsed_time; /* Duration in CPU clock ticks of the program */
110 ULONGLONG total_duration; /* Total CPU clock ticks of all the threads */
111 ULONGLONG pal_duration; /* Total CPU clock ticks spent inside PAL */
113 #ifndef PLATFORM_UNIX
118 char start_time[32]; /* must be at least 26 characters */
119 } pal_perf_program_info;
121 #ifndef PLATFORM_UNIX
122 typedef FILE PERF_FILE;
123 #define PERF_FILEFN(x) x
125 typedef PAL_FILE PERF_FILE;
126 #define PERF_FILEFN(x) PAL_ ## x
129 static ULONGLONG PERFGetTicks();
130 static double PERFComputeStandardDeviation(pal_perf_api_info *api);
131 static void PERFPrintProgramHeaderInfo(PERF_FILE * hFile, BOOL completedExecution);
132 static BOOL PERFInitProgramInfo(LPWSTR command_line, LPWSTR exe_path);
133 static BOOL PERFReadSetting( );
134 static void PERFLogFileName(PathCharString * destFileString, const char *fileName, const char *suffix, int max_length);
135 static void PERFlushAllLogs();
136 static int PERFWriteCounters(pal_perf_api_info * table);
137 static BOOL PERFFlushLog(pal_perf_thread_info * local_buffer, BOOL output_header);
138 static void PERFUpdateApiInfo(pal_perf_api_info *api, ULONGLONG duration);
139 static char * PERFIsValidPath( const char * path );
140 static char * PERFIsValidFile( const char * path, const char * file);
142 typedef char PAL_API_NAME[PAL_PERF_MAX_FUNCTION_NAME];
144 static PAL_API_NAME API_list[PAL_API_NUMBER] ;
145 static pal_perf_program_info program_info;
147 #ifndef PLATFORM_UNIX
148 static DWORD PERF_tlsTableKey=0 ;
150 static pthread_key_t PERF_tlsTableKey=0 ;
153 static pal_thread_list_node * process_pal_thread_list=NULL;
154 static BOOL pal_profile_on=FALSE;
155 static BOOL pal_perf_enabled=FALSE;
156 static char * pal_function_map=NULL;
157 static char * perf_default_path=NULL;
158 static char * traced_apis_file=NULL;
159 static char * enabledapis_path=NULL;
160 static char * profile_log_path=NULL;
161 static char * profile_summary_log_name=NULL;
162 static char * profile_time_log_name=NULL;
163 static BOOL summary_only=FALSE;
164 static BOOL nested_tracing=FALSE;
165 static BOOL calibrate=FALSE;
167 /* If report_only_called_apis is TRUE,
168 those PAL APIs with no function entry or exit
169 will not be shown in the PAL perf summary file. */
170 static BOOL report_only_called_apis=FALSE;
172 /* If the wait_for_startup is TRUE, process profiling
173 will not start until the application
174 has called PAL_EnableProcessProfile(). */
175 static BOOL wait_for_startup=FALSE;
177 /* The size of a PAL API execution CPU ticks histogram, i.e.,
178 Number of categories of frequency distrubution of PAL API
179 execution CPU ticks.*/
180 static DWORD pal_perf_histogram_size = 0;
182 /* The step size in CPU ticks of each category of the
183 PAL API execution CPU ticks histogram.*/
184 static DWORD pal_perf_histogram_step = 100;
186 static const char PAL_PERF_TRACING[]="PAL_PERF_TRACING";
187 static const char PAL_DEFAULT_PATH[]="PAL_PERF_DEFAULT_PATH";
188 static const char PAL_PERF_TRACEDAPIS_PATH[]="PAL_PERF_TRACEDAPIS_FILE";
189 static const char PAL_PERF_LOG_PATH[]="PAL_PERF_LOG_PATH";
190 static const char PAL_PERF_SUMMARY_LOG_NAME[]="PAL_PERF_SUMMARY_LOG_NAME";
191 static const char PAL_PERF_TIME_LOG_NAME[]="PAL_PERF_TIME_LOG_NAME";
192 static const char PAL_PERF_ENABLED_APIS_PATH[]="PAL_PERF_ENABLEDAPIS_FILE";
193 static const char PAL_SUMMARY_FLAG[]="PAL_PERF_SUMMARY_ONLY";
194 static const char PAL_PERF_NESTED_TRACING[]="PAL_PERF_NESTED_TRACING";
195 static const char PAL_PERF_CALIBRATE[]="PAL_PERF_CALIBRATE";
196 static const char PAL_PERF_REPORT_ONLY_CALLED_APIS[]="PAL_PERF_REPORT_ONLY_CALLED_APIS";
197 static const char PAL_PERF_WAIT_FOR_STARTUP[]="PAL_PERF_WAIT_FOR_STARTUP";
198 static const char PAL_PERF_HISTOGRAM_SIZE[]="PAL_PERF_HISTOGRAM_SIZE";
199 static const char PAL_PERF_HISTOGRAM_STEP[]="PAL_PERF_HISTOGRAM_STEP";
200 static const char traced_apis_filename[]="PerfTracedAPIs.txt";
201 static const char perf_enabled_filename[]="AllPerfEnabledAPIs.txt";
202 #ifndef PLATFORM_UNIX
203 static const char PATH_SEPARATOR[] = "\\";
205 static const char PATH_SEPARATOR[] = "/";
210 #ifndef PLATFORM_UNIX
211 #define LLFORMAT "%I64u"
213 #define LLFORMAT "%llu"
219 #ifdef _X86_ // for BSD and Windows.
229 asm volatile("rdtsc":"=a" (a), "=d" (d));
230 #define volatile DoNotUseVolatileKeyword
232 return ((ULONGLONG)((unsigned int)(d)) << 32) | (unsigned int)(a);
234 return 0; // on non-BSD and non-Windows, we'll return 0 for now.
240 PERFComputeStandardDeviation(pal_perf_api_info *api)
243 double sum_of_variance;
244 if (api->counter <= 1)
246 n = (double) api->counter;
247 // Calculates standard deviation based on the entire population given as arguments.
248 // Same as stdevp in Excel.
249 sum_of_variance = (n*api->sum_of_square_duration) - (api->sum_duration*api->sum_duration);
250 if (sum_of_variance <= 0.0)
252 return sqrt(sum_of_variance/(n*n));
258 PERFPrintProgramHeaderInfo(PERF_FILE * hFile, BOOL completedExecution)
263 if (completedExecution) {
264 etime = program_info.elapsed_time;
265 ttime = program_info.total_duration;
266 ptime = program_info.pal_duration;
268 PERF_FILEFN(fprintf)(hFile,"#LOG\tversion=1.00\n");
270 PERF_FILEFN(fprintf)(hFile, "#MACHINE\thostname=%s\tcpu_clock_frequency=%g\n", program_info.hostname,
271 program_info.cpu_clock_frequency);
272 PERF_FILEFN(fprintf)(hFile, "#PROCESS\tprocess_id=%d\ttotal_latency=" LLFORMAT "\tthread_times=" LLFORMAT "\tpal_time=" LLFORMAT "\texe_path=%s\tcommand_line=%s\tstart_time=%s",
273 program_info.process_id, etime, ttime, ptime,
274 program_info.exe_path,program_info.command_line,program_info.start_time);
279 PERFInitProgramInfo(LPWSTR command_line, LPWSTR exe_path)
281 ULONGLONG start_tick;
282 #ifndef PLATFORM_UNIX
288 if (WideCharToMultiByte(CP_ACP, 0, command_line, -1,
289 program_info.command_line, PAL_PERF_MAX_LOGLINE-1, NULL, NULL) == 0)
291 if (WideCharToMultiByte(CP_ACP, 0, exe_path, -1,
292 program_info.exe_path, PAL_PERF_MAX_LOGLINE-1, NULL, NULL) == 0)
295 gethostname(program_info.hostname, PAL_PERF_MAX_FUNCTION_NAME);
296 program_info.process_id = getpid();
298 #ifndef PLATFORM_UNIX
300 strcpy(program_info.start_time, ctime( &tv ));
302 gettimeofday(&tv, NULL);
303 ctime_r(&tv.tv_sec, program_info.start_time);
306 // estimate the cpu clock cycles
307 start_tick = PERFGetTicks();
310 #ifndef PLATFORM_UNIX
311 Sleep(1000); //Sleep on Windows takes milliseconds as argument
315 program_info.cpu_clock_frequency = (double) (PERFGetTicks() - start_tick);
319 program_info.cpu_clock_frequency = 0.0;
322 program_info.start_ticks = 0;
323 program_info.elapsed_time = 0;
324 program_info.total_duration = 0;
325 program_info.pal_duration = 0;
332 PERFCalibrationFunction()
334 PERF_ENTRY(CalibrationFunction);
335 PERF_EXIT(CalibrationFunction);
339 PERFCalibrate(const char* msg)
341 ULONGLONG start_tick, cal_ticks;
343 int cal_length=100000;
346 start_tick = PERFGetTicks();
347 for(i=0; i<cal_length; i++)
349 PERFCalibrationFunction();
351 cal_ticks = PERFGetTicks() - start_tick;
352 printf("%s: %g\n", msg, (double)(cal_ticks/cal_length));
357 PERFInitialize(LPWSTR command_line, LPWSTR exe_path)
362 // Check if PAL Perf should be disabled
363 char *pal_perf_tracing_env = MiscGetenv(PAL_PERF_TRACING);
364 if ( pal_perf_tracing_env == NULL || strlen(pal_perf_tracing_env) == 0)
366 pal_perf_enabled = FALSE;
371 pal_perf_enabled = TRUE;
373 if (!PERFInitProgramInfo(command_line, exe_path))
376 pal_profile_on = FALSE; // turn it off until we setup everything.
377 // allocate the TLS index for structures
378 #ifndef PLATFORM_UNIX
379 if( ( PERF_tlsTableKey = TlsAlloc() ) == -1 )
382 if( pthread_key_create(&PERF_tlsTableKey , NULL) != 0 )
388 pal_function_map = (char*)PAL_malloc(PAL_API_NUMBER);
389 if(pal_function_map != NULL)
391 bRead = PERFReadSetting( ); // we don't quit even we failed to read the file.
394 /* free the index in TLS */
398 #ifndef PLATFORM_UNIX
399 TlsFree(PERF_tlsTableKey );
401 pthread_key_delete(PERF_tlsTableKey );
407 PERFCalibrate("Overhead when profiling is disabled process-wide");
413 void PERFTerminate( )
415 static LONG pal_perf_terminated = FALSE;
417 if (!pal_perf_enabled || wait_for_startup)
420 // make sure PERFTerminate is called only once
421 if (InterlockedCompareExchange(&pal_perf_terminated, TRUE, FALSE))
425 #ifndef PLATFORM_UNIX
426 TlsFree(PERF_tlsTableKey );
428 pthread_key_delete(PERF_tlsTableKey );
430 PAL_free(pal_function_map);
434 BOOL PERFAllocThreadInfo( )
436 pal_perf_api_info * apiTable = NULL;
437 pal_thread_list_node * node = NULL;
438 pal_perf_thread_info * local_info = NULL;
439 char * log_buf = NULL;
443 if (!pal_perf_enabled)
446 /* The memory allocated per thread for PAL perf tracing is never freed until PAL_Terminate
447 is called in the current implementation. If the test program keeps creating new threads,
448 memory resources could be exhausted. If this ever becomes a problem, the memory allocated
449 per thread should be freed when a thread exits. */
451 node = ( pal_thread_list_node * )PAL_malloc(sizeof(pal_thread_list_node));
455 goto PERFAllocThreadInfoExit;
458 local_info = (pal_perf_thread_info *)PAL_malloc(sizeof(pal_perf_thread_info));
459 if (local_info == NULL)
462 goto PERFAllocThreadInfoExit;
465 apiTable = (pal_perf_api_info *)PAL_malloc( PAL_API_NUMBER * sizeof(pal_perf_api_info));
466 if (apiTable == NULL)
469 goto PERFAllocThreadInfoExit;
472 node->thread_info = local_info;
473 local_info->api_table=apiTable;
474 local_info->threadId = THREADSilentGetCurrentThreadId();
476 for (i = 0; i < PAL_API_NUMBER; i++)
478 apiTable[i].entries = 0;
479 apiTable[i].counter = 0;
480 apiTable[i].min_duration = _UI64_MAX;
481 apiTable[i].max_duration = 0;
482 apiTable[i].sum_duration = 0;
483 apiTable[i].sum_of_square_duration = 0.0;
484 if (pal_perf_histogram_size > 0)
486 apiTable[i].histograms = (DWORD *)PAL_malloc(pal_perf_histogram_size*sizeof(DWORD));
487 if (apiTable[i].histograms == NULL)
490 goto PERFAllocThreadInfoExit;
492 memset(apiTable[i].histograms, 0, pal_perf_histogram_size*sizeof(DWORD));
496 apiTable[i].histograms = NULL;
500 log_buf = (char * )PAL_malloc( PAL_PERF_PROFILE_BUFFER_SIZE );
505 goto PERFAllocThreadInfoExit;
508 local_info->pal_write_buf=log_buf;
509 local_info->buf_offset = 0;
510 local_info->profile_enabled = FALSE;
511 local_info->total_duration = 0;
512 local_info->start_ticks = 0;
513 memset(log_buf, 0, PAL_PERF_PROFILE_BUFFER_SIZE);
515 #ifndef PLATFORM_UNIX
516 if ( TlsSetValue(PERF_tlsTableKey, local_info) == 0)
519 if (pthread_setspecific(PERF_tlsTableKey, local_info) != 0)
523 PERFAllocThreadInfoExit:
526 node->next = process_pal_thread_list;
527 process_pal_thread_list = node;
528 PERFFlushLog(local_info, TRUE);
536 if (local_info != NULL)
538 PAL_free(local_info);
540 if (apiTable != NULL)
542 for (i = 0; i < PAL_API_NUMBER; i++)
544 if (apiTable[i].histograms != NULL)
546 PAL_free(apiTable[i].histograms);
561 PERFUpdateProgramInfo(pal_perf_thread_info* local_info)
565 if (!local_info) return;
567 // add the elapsed time to the program's total
568 if (local_info->total_duration == 0)
570 // this thread did not go through PERFDisableThreadProfile code
571 // so compute the total elapsed time for the thread here
572 local_info->total_duration = PERFGetTicks() - local_info->start_ticks;
574 program_info.total_duration += local_info->total_duration;
576 // Add up all the time spent in PAL
577 if (local_info->api_table) {
578 for(i=0; i<PAL_API_NUMBER; i++) {
579 program_info.pal_duration += local_info->api_table[i].sum_duration;
589 pal_thread_list_node * current, * node;
590 pal_perf_api_info * table1, *table0;
592 node = process_pal_thread_list;
593 if(node == NULL || node->thread_info == NULL || node->thread_info->api_table == NULL ) // should not come here
597 process_pal_thread_list = process_pal_thread_list->next;
598 table0 = node->thread_info->api_table;
600 PERFUpdateProgramInfo(node->thread_info);
602 while(process_pal_thread_list)
604 current=process_pal_thread_list;
605 process_pal_thread_list = process_pal_thread_list->next;
606 if (current->thread_info)
608 if (current->thread_info->api_table)
610 table1 = current->thread_info->api_table;
611 for(i=0;i<PAL_API_NUMBER;i++)
614 if (table1[i].counter == 0)
618 for (j = 0; j < pal_perf_histogram_size; j++)
620 table0[i].histograms[j] += table1[i].histograms[j];
622 table0[i].entries += table1[i].entries;
623 table0[i].counter += table1[i].counter;
624 if (table0[i].min_duration > table1[i].min_duration)
625 table0[i].min_duration = table1[i].min_duration;
626 if (table0[i].max_duration < table1[i].max_duration)
627 table0[i].max_duration = table1[i].max_duration;
628 table0[i].sum_duration += table1[i].sum_duration;
629 table0[i].sum_of_square_duration += table1[i].sum_of_square_duration;
631 PERFUpdateProgramInfo(current->thread_info);
632 if (table1->histograms != NULL)
634 PAL_free(table1->histograms);
638 PERFFlushLog(current->thread_info, FALSE);
639 PAL_free(current->thread_info->pal_write_buf);
640 PAL_free(current->thread_info);
644 PERFWriteCounters(table0);
645 if (table0->histograms != NULL)
647 PAL_free(table0->histograms);
650 PERFFlushLog(node->thread_info, FALSE);
651 PAL_free(node->thread_info->pal_write_buf);
652 PAL_free(node->thread_info);
658 PERFLogFileName(PathCharString& destFileString, const char *fileName, const char *suffix)
660 const char *dir_path;
661 CPalThread* pThread = InternalGetCurrentThread();
662 dir_path = (profile_log_path == NULL) ? "." : profile_log_path;
664 destFileString.Append(dir_path, strlen(dir_path));
665 destFileString.Append(PATH_SEPARATOR, strlen(PATH_SEPARATOR));
666 if (fileName != NULL)
668 destFileString.Append(fileName, strlen(fileName));
673 char* process_id = itoa(program_info.process_id, buffer, 10);
674 destFileString.Append(process_id, strlen(process_id));
675 destFileString.Append("_", 1);
677 char* current_thread = itoa(THREADSilentGetCurrentThreadId(),buffer, 10);
678 destFileString.Append(current_thread, strlen( current_thread));
679 destFileString.Append(suffix, strlen(suffix));
686 PERFWriteCounters( pal_perf_api_info * table )
688 PathCharString fileName;
689 pal_perf_api_info * off;
695 PERFLogFileName(fileName, profile_summary_log_name, "_perf_summary.log");
696 hFile = PERF_FILEFN(fopen)(fileName, "a+");
699 PERFPrintProgramHeaderInfo(hFile, TRUE);
700 PERF_FILEFN(fprintf)(hFile,"#api_name\tapi_id\tperf_entries\tperf_exits\tsum_of_latency\tmin_latency\tmax_latency\tstd_dev_latency\tsum_of_square_latency\n");
701 for(i=0;i<PAL_API_NUMBER;i++)
704 ULONGLONG min_duration;
706 min_duration = (off->min_duration == _UI64_MAX) ? 0 : off->min_duration;
707 if (off->counter >= 1)
709 dev = PERFComputeStandardDeviation(off);
716 if (off->counter > 0 || !report_only_called_apis)
718 PERF_FILEFN(fprintf)(hFile,"%s\t%d\t" LLFORMAT "\t" LLFORMAT "\t" LLFORMAT "\t" LLFORMAT "\t" LLFORMAT "\t%g\t%g\n",
719 API_list[i], i, off->entries, off->counter,off->sum_duration,
720 min_duration, off->max_duration, dev, off->sum_of_square_duration);
730 PERF_FILEFN(fclose)(hFile);
732 if (pal_perf_histogram_size > 0)
735 PERFLogFileName(fileName, profile_summary_log_name, "_perf_summary.hist");
736 hFile = PERF_FILEFN(fopen)(fileName, "a+");
741 PERF_FILEFN(fprintf)(hFile,"#api_name\tapi_id");
742 for (j = 0; j < pal_perf_histogram_size; j++)
744 PERF_FILEFN(fprintf)(hFile, "\t%d", j*pal_perf_histogram_step);
746 PERF_FILEFN(fprintf)(hFile, "\n");
748 for(i = 0; i < PAL_API_NUMBER; i++)
750 if (off->counter > 0)
752 PERF_FILEFN(fprintf)(hFile,"%s\t%d", API_list[i], i);
754 for (j = 0; j < pal_perf_histogram_size; j++)
756 PERF_FILEFN(fprintf)(hFile, "\t%d", off->histograms[j]);
759 PERF_FILEFN(fprintf)(hFile, "\n");
769 PERF_FILEFN(fclose)(hFile);
779 // this function is not safe right now.
780 //more code is required to deal with corrupted input file.
783 char line[PAL_PERF_MAX_INPUT];
785 char function_name[PAL_PERF_MAX_FUNCTION_NAME]; //no function can be longer than 127 bytes.
787 char * file_name_buf;
788 PathCharString file_name_bufPS;
789 char * input_file_name;
790 char * summary_flag_env;
791 char * nested_tracing_env;
792 char * calibrate_env;
793 char * report_only_called_apis_env;
794 char * wait_for_startup_env;
795 char * pal_perf_histogram_size_env;
796 char * pal_perf_histogram_step_env;
804 if((pal_function_map == NULL) || (PAL_API_NUMBER < 0) )
806 // should not be here.
809 /* do some env setting here */
810 summary_flag_env = MiscGetenv(PAL_SUMMARY_FLAG);
811 if (summary_flag_env == NULL || strlen(summary_flag_env) == 0)
813 summary_only = FALSE;
819 nested_tracing_env = MiscGetenv(PAL_PERF_NESTED_TRACING);
820 if (nested_tracing_env == NULL || strlen(nested_tracing_env) == 0)
822 nested_tracing = FALSE;
826 nested_tracing = TRUE;
829 calibrate_env = MiscGetenv(PAL_PERF_CALIBRATE);
830 if (calibrate_env == NULL || strlen(calibrate_env) == 0)
839 report_only_called_apis_env = MiscGetenv(PAL_PERF_REPORT_ONLY_CALLED_APIS);
840 if (report_only_called_apis_env == NULL || strlen(report_only_called_apis_env) == 0)
842 report_only_called_apis = FALSE;
846 report_only_called_apis = TRUE;
849 wait_for_startup_env = MiscGetenv(PAL_PERF_WAIT_FOR_STARTUP);
850 if (wait_for_startup_env == NULL || strlen(wait_for_startup_env) == 0)
852 wait_for_startup = FALSE;
856 wait_for_startup = TRUE;
859 pal_perf_histogram_size_env = MiscGetenv(PAL_PERF_HISTOGRAM_SIZE);
860 if (pal_perf_histogram_size_env != NULL && strlen(pal_perf_histogram_size_env) > 0)
864 value = strtol(pal_perf_histogram_size_env, &endptr, 10);
867 pal_perf_histogram_size = (DWORD) value;
871 pal_perf_histogram_step_env = MiscGetenv(PAL_PERF_HISTOGRAM_STEP);
872 if (pal_perf_histogram_step_env != NULL && strlen(pal_perf_histogram_step_env) > 0)
876 value = strtol(pal_perf_histogram_step_env, &endptr, 10);
879 pal_perf_histogram_step = (DWORD) value;
883 traced_apis_file = PERFIsValidFile("", MiscGetenv(PAL_PERF_TRACEDAPIS_PATH));
884 enabledapis_path = PERFIsValidFile("", MiscGetenv(PAL_PERF_ENABLED_APIS_PATH));
885 profile_log_path = PERFIsValidPath(MiscGetenv(PAL_PERF_LOG_PATH));
886 perf_default_path = PERFIsValidPath( MiscGetenv(PAL_DEFAULT_PATH));
887 profile_summary_log_name = MiscGetenv(PAL_PERF_SUMMARY_LOG_NAME);
888 if (profile_summary_log_name != NULL && strlen(profile_summary_log_name) == 0)
889 profile_summary_log_name = NULL;
890 profile_time_log_name = MiscGetenv(PAL_PERF_TIME_LOG_NAME);
891 if (profile_time_log_name != NULL && strlen(profile_time_log_name) == 0)
892 profile_time_log_name = NULL;
894 if( traced_apis_file == NULL)
896 if(perf_default_path==NULL)
899 input_file_name = NULL;
903 if( PERFIsValidFile(perf_default_path,traced_apis_filename))
905 int length = strlen(perf_default_path) + strlen(PATH_SEPARATOR) + strlen(traced_apis_filename);
906 file_name_buf = file_name_bufPS.OpenStringBuffer(length);
907 if ((strcpy_s(file_name_buf, file_name_bufPS.GetSizeOf(), perf_default_path) != SAFECRT_SUCCESS) ||
908 (strcat_s(file_name_buf, file_name_bufPS.GetSizeOf(), PATH_SEPARATOR) != SAFECRT_SUCCESS) ||
909 (strcat_s(file_name_buf, file_name_bufPS.GetSizeOf(), traced_apis_filename) != SAFECRT_SUCCESS))
911 file_name_bufPS.CloseBuffer(0);
913 input_file_name = NULL;
917 file_name_bufPS.CloseBuffer(length);
918 input_file_name = file_name_buf;
924 input_file_name=NULL;
930 input_file_name=traced_apis_file;
936 hFile = PAL_fopen(input_file_name, "r+");
938 hFile = fopen(input_file_name, "r+");
942 memset(pal_function_map, 1, PAL_API_NUMBER);
947 memset(pal_function_map, 0, PAL_API_NUMBER);
949 PAL_fseek(hFile, 0L, SEEK_SET);
951 /* Read a line of data from file: */
952 while ( PAL_fgets(line, PAL_PERF_MAX_INPUT, hFile) != NULL )
956 ptr = strchr( line, '#');
959 sscanf_s(line, "%s %u", function_name,&index);
961 if( index >= PAL_API_NUMBER)
963 // some code here to deal with incorrect index.
964 // use function name to cover it.
966 else if(pal_function_map[index]==1)
968 // some code here to deal with conflict index.
969 // use function name to cover it.
973 pal_function_map[index]=1;
988 memset(pal_function_map, 1, PAL_API_NUMBER);
992 if( enabledapis_path == NULL)
994 if(perf_default_path==NULL)
996 input_file_name = NULL;
1000 if( PERFIsValidFile(perf_default_path,perf_enabled_filename))
1002 if ((strcpy_s(file_name_buf, sizeof(file_name_buf), perf_default_path) != SAFECRT_SUCCESS) ||
1003 (strcat_s(file_name_buf, sizeof(file_name_buf), PATH_SEPARATOR) != SAFECRT_SUCCESS) ||
1004 (strcat_s(file_name_buf, sizeof(file_name_buf), perf_enabled_filename) != SAFECRT_SUCCESS))
1007 input_file_name = NULL;
1011 input_file_name = file_name_buf;
1016 input_file_name=NULL;
1022 input_file_name=enabledapis_path;
1025 if(input_file_name == NULL)
1030 #ifdef PLATFORM_UNIX
1031 hFile = PAL_fopen(input_file_name, "r+");
1033 hFile = fopen(input_file_name, "r+");
1036 if ( hFile != NULL )
1038 PAL_fseek(hFile, 0L, SEEK_SET);
1040 /* Read a line of data from file: */
1041 while (PAL_fgets(line, PAL_PERF_MAX_INPUT, hFile) != NULL)
1045 ptr = strchr( line, '#');
1048 sscanf_s(line, "%s %u", function_name,&index);
1050 if( index >= PAL_API_NUMBER)
1052 // some code here to deal with incorrect index.
1053 // use function name to cover it.
1057 if (strcpy_s(API_list[index], sizeof(API_list[index]), function_name) != SAFECRT_SUCCESS)
1064 #ifdef PLATFORM_UNIX
1078 PERFFlushLog(pal_perf_thread_info * local_info, BOOL output_header)
1081 PathCharString fileName;
1082 int nWrittenBytes = 0;
1088 PERFLogFileName(fileName, profile_time_log_name, "_perf_time.log");
1090 hFile = PERF_FILEFN(fopen)(fileName, "a+");
1096 PERFPrintProgramHeaderInfo(hFile, FALSE);
1098 if (local_info->buf_offset > 0)
1100 nWrittenBytes = PERF_FILEFN(fwrite)(local_info->pal_write_buf, local_info->buf_offset, 1, hFile);
1101 if (nWrittenBytes < 1)
1103 ERROR("fwrite() failed with errno == %d\n", errno);
1106 local_info->buf_offset = 0;
1108 PERF_FILEFN(fclose)(hFile);
1116 PERFLogFunctionEntry(unsigned int pal_api_id, ULONGLONG *pal_perf_start_tick )
1118 pal_perf_thread_info * local_info=NULL;
1119 pal_perf_api_info * table;
1125 #ifndef PLATFORM_UNIX
1128 last_error = GetLastError();
1134 if(!pal_perf_enabled || pal_function_map==NULL || !pal_profile_on ) // haven't initialize, just quit.
1137 if( pal_function_map[pal_api_id] )
1139 local_info= (pal_perf_thread_info * )pthread_getspecific(PERF_tlsTableKey);
1141 if (local_info==NULL )
1145 if ( !local_info->profile_enabled ) /* prevent recursion. */
1149 // turn on this flag before call any other functions
1150 local_info->profile_enabled = FALSE;
1151 table = local_info->api_table;
1152 table[pal_api_id].entries++;
1156 write_buf = (local_info->pal_write_buf);
1157 if(local_info->buf_offset >= PAL_PERF_BUFFER_FULL)
1159 PERFFlushLog(local_info, FALSE);
1162 #ifndef PLATFORM_UNIX
1163 tv = GetTickCount();
1165 gettimeofday(&tv, NULL);
1168 buf_off = local_info->buf_offset;
1170 #ifndef PLATFORM_UNIX
1171 bufused = snprintf(&write_buf[buf_off], PAL_PERF_MAX_LOGLINE, "----> %d %lu entry.\n", pal_api_id, tv );
1173 bufused = snprintf(&write_buf[buf_off], PAL_PERF_MAX_LOGLINE, "----> %d %lu %06u entry.\n", pal_api_id, tv.tv_sec, tv.tv_usec );
1175 local_info->buf_offset += bufused;
1178 local_info->profile_enabled = TRUE;
1179 *pal_perf_start_tick = PERFGetTicks();
1181 #ifndef PLATFORM_UNIX
1182 SetLastError( last_error );
1189 PERFUpdateApiInfo(pal_perf_api_info *api, ULONGLONG duration)
1194 if (api->min_duration > duration)
1195 api->min_duration = duration;
1196 if (api->max_duration < duration)
1197 api->max_duration = duration;
1198 api->sum_duration += duration;
1199 api->sum_of_square_duration += (double) duration * (double)duration;
1201 if (pal_perf_histogram_size > 0)
1203 iBucket = (DWORD)(duration / pal_perf_histogram_step);
1204 if (iBucket >= pal_perf_histogram_size)
1206 iBucket = pal_perf_histogram_size - 1;
1208 api->histograms[iBucket]++;
1214 PERFLogFunctionExit(unsigned int pal_api_id, ULONGLONG *pal_perf_start_tick )
1217 pal_perf_thread_info * local_info;
1221 ULONGLONG duration = 0;
1222 #ifndef PLATFORM_UNIX
1225 last_error = GetLastError();
1227 struct timeval timev;
1231 if(!pal_perf_enabled || (pal_function_map == NULL) || !pal_profile_on ) // haven't initiallize yet, just quit.
1234 if (*pal_perf_start_tick != 0)
1236 duration = PERFGetTicks() - *pal_perf_start_tick;
1240 return; // pal_perf_start_tick == 0 indicates that we exited PERFLogFunctionEntry before getting the ticks.
1243 if( pal_function_map[pal_api_id] )
1245 local_info = (pal_perf_thread_info*)pthread_getspecific(PERF_tlsTableKey);
1247 if (NULL == local_info ){
1250 PERFUpdateApiInfo(&local_info->api_table[pal_api_id], duration);
1251 *pal_perf_start_tick = 0;
1255 local_info->profile_enabled = TRUE;
1256 #ifndef PLATFORM_UNIX
1257 SetLastError( last_error );
1262 #ifndef PLATFORM_UNIX
1263 timev = GetTickCount();
1265 gettimeofday(&timev, NULL);
1268 buf = local_info->pal_write_buf;
1269 if(local_info->buf_offset >= PAL_PERF_BUFFER_FULL)
1271 PERFFlushLog(local_info, FALSE);
1273 off = local_info->buf_offset;
1275 #ifndef PLATFORM_UNIX
1276 bufused = snprintf(&buf[off], PAL_PERF_MAX_LOGLINE, "<---- %d %lu exit. \n", pal_api_id, timev);
1278 bufused = snprintf(&buf[off], PAL_PERF_MAX_LOGLINE, "<---- %d %lu %06u exit. \n", pal_api_id, timev.tv_sec, timev.tv_usec );
1280 local_info->buf_offset += bufused;
1281 local_info->profile_enabled = TRUE;
1283 #ifndef PLATFORM_UNIX
1284 SetLastError( last_error );
1290 PERFNoLatencyProfileEntry(unsigned int pal_api_id )
1292 pal_perf_thread_info * local_info=NULL;
1293 pal_perf_api_info * table;
1294 #ifndef PLATFORM_UNIX
1296 last_error = GetLastError();
1299 if(!pal_perf_enabled || pal_function_map==NULL || !pal_profile_on ) // haven't initialize, just quit.
1301 if( pal_function_map[pal_api_id] )
1303 local_info= (pal_perf_thread_info * )pthread_getspecific(PERF_tlsTableKey);
1304 if (local_info==NULL )
1306 #ifndef PLATFORM_UNIX
1307 SetLastError( last_error );
1312 table = local_info->api_table;
1313 table[pal_api_id].entries++;
1316 #ifndef PLATFORM_UNIX
1317 SetLastError( last_error );
1324 PERFEnableThreadProfile(BOOL isInternal)
1326 pal_perf_thread_info * local_info;
1327 #ifndef PLATFORM_UNIX
1329 last_error = GetLastError();
1331 if (!pal_perf_enabled)
1333 if (NULL != (local_info = (pal_perf_thread_info*)pthread_getspecific(PERF_tlsTableKey)))
1335 if (!isInternal || nested_tracing) {
1336 local_info->profile_enabled = TRUE;
1337 local_info->start_ticks = PERFGetTicks();
1340 #ifndef PLATFORM_UNIX
1341 SetLastError( last_error );
1347 PERFDisableThreadProfile(BOOL isInternal)
1349 pal_perf_thread_info * local_info;
1350 #ifndef PLATFORM_UNIX
1352 last_error = GetLastError();
1354 if (!pal_perf_enabled)
1356 if (NULL != (local_info = (pal_perf_thread_info*)pthread_getspecific(PERF_tlsTableKey)))
1358 if (!isInternal || nested_tracing) {
1359 local_info->profile_enabled = FALSE;
1360 local_info->total_duration = PERFGetTicks() - local_info->start_ticks;
1363 #ifndef PLATFORM_UNIX
1364 SetLastError( last_error );
1370 PERFEnableProcessProfile( )
1372 if (!pal_perf_enabled || wait_for_startup)
1374 pal_profile_on = TRUE;
1375 PERFCalibrate("Overhead when profiling is disabled temporarily for a thread");
1376 // record the cpu clock ticks at the beginning of the profiling.
1377 program_info.start_ticks = PERFGetTicks();
1382 PERFDisableProcessProfile( )
1384 if (!pal_perf_enabled)
1386 pal_profile_on = FALSE;
1387 // compute the total program duration in cpu clock ticks.
1388 if (program_info.start_ticks != 0)
1390 program_info.elapsed_time += (PERFGetTicks() - program_info.start_ticks);
1391 program_info.start_ticks = 0;
1396 PERFIsProcessProfileEnabled( )
1398 return pal_profile_on;
1403 PERFIsValidPath( const char * path )
1405 #ifndef PLATFORM_UNIX
1411 if(( path==NULL) || (strlen(path)==0))
1414 #ifndef PLATFORM_UNIX
1415 result = GetFileAttributesA( path );
1416 if ((result != INVALID_FILE_ATTRIBUTES) && (result & FILE_ATTRIBUTE_DIRECTORY))
1418 return ((char *) path );
1421 dir = opendir(path);
1425 return ((char *)path);
1433 PERFIsValidFile( const char * path, const char * file)
1437 PathCharString tempPS;
1439 if(file==NULL || strlen(file)==0)
1442 if ( strcmp(path, "") )
1444 int length = strlen(path) + strlen(PATH_SEPARATOR) + strlen(file);
1445 temp = tempPS.OpenStringBuffer(length);
1446 if ((strcpy_s(temp, sizeof(temp), path) != SAFECRT_SUCCESS) ||
1447 (strcat_s(temp, sizeof(temp), PATH_SEPARATOR) != SAFECRT_SUCCESS) ||
1448 (strcat_s(temp, sizeof(temp), file) != SAFECRT_SUCCESS))
1450 tempPS.CloseBuffer(0);
1454 tempPS.CloseBuffer(length);
1455 hFile = fopen(temp, "r");
1459 hFile = fopen(file, "r");
1465 return ((char *) file);
1475 PAL_EnableProcessProfile(VOID)
1477 wait_for_startup = FALSE;
1478 pal_profile_on = TRUE;
1479 PERFEnableProcessProfile();
1485 PAL_DisableProcessProfile(VOID)
1487 pal_profile_on = FALSE;
1488 PERFDisableProcessProfile();
1494 PAL_IsProcessProfileEnabled(VOID)
1496 return PERFIsProcessProfileEnabled();
1502 PAL_GetCpuTickCount(VOID)
1504 return PERFGetTicks();
1507 #ifndef PLATFORM_UNIX
1510 #undef pthread_key_t
1511 #undef pthread_getspecific
1512 #endif /* ifndef PLATFORM_UNIX definitions */
1514 #endif /* PAL_PERF */