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);
235 return (ULONGLONG)gethrtime();
237 return 0; // on non-BSD and non-Windows, we'll return 0 for now.
244 PERFComputeStandardDeviation(pal_perf_api_info *api)
247 double sum_of_variance;
248 if (api->counter <= 1)
250 n = (double) api->counter;
251 // Calculates standard deviation based on the entire population given as arguments.
252 // Same as stdevp in Excel.
253 sum_of_variance = (n*api->sum_of_square_duration) - (api->sum_duration*api->sum_duration);
254 if (sum_of_variance <= 0.0)
256 return sqrt(sum_of_variance/(n*n));
262 PERFPrintProgramHeaderInfo(PERF_FILE * hFile, BOOL completedExecution)
267 if (completedExecution) {
268 etime = program_info.elapsed_time;
269 ttime = program_info.total_duration;
270 ptime = program_info.pal_duration;
272 PERF_FILEFN(fprintf)(hFile,"#LOG\tversion=1.00\n");
274 PERF_FILEFN(fprintf)(hFile, "#MACHINE\thostname=%s\tcpu_clock_frequency=%g\n", program_info.hostname,
275 program_info.cpu_clock_frequency);
276 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",
277 program_info.process_id, etime, ttime, ptime,
278 program_info.exe_path,program_info.command_line,program_info.start_time);
283 PERFInitProgramInfo(LPWSTR command_line, LPWSTR exe_path)
285 ULONGLONG start_tick;
286 #ifndef PLATFORM_UNIX
292 if (WideCharToMultiByte(CP_ACP, 0, command_line, -1,
293 program_info.command_line, PAL_PERF_MAX_LOGLINE-1, NULL, NULL) == 0)
295 if (WideCharToMultiByte(CP_ACP, 0, exe_path, -1,
296 program_info.exe_path, PAL_PERF_MAX_LOGLINE-1, NULL, NULL) == 0)
299 gethostname(program_info.hostname, PAL_PERF_MAX_FUNCTION_NAME);
300 program_info.process_id = getpid();
302 #ifndef PLATFORM_UNIX
304 strcpy(program_info.start_time, ctime( &tv ));
306 gettimeofday(&tv, NULL);
307 ctime_r(&tv.tv_sec, program_info.start_time);
310 // estimate the cpu clock cycles
311 start_tick = PERFGetTicks();
314 #ifndef PLATFORM_UNIX
315 Sleep(1000); //Sleep on Windows takes milliseconds as argument
319 program_info.cpu_clock_frequency = (double) (PERFGetTicks() - start_tick);
323 program_info.cpu_clock_frequency = 0.0;
326 program_info.start_ticks = 0;
327 program_info.elapsed_time = 0;
328 program_info.total_duration = 0;
329 program_info.pal_duration = 0;
336 PERFCalibrationFunction()
338 PERF_ENTRY(CalibrationFunction);
339 PERF_EXIT(CalibrationFunction);
343 PERFCalibrate(const char* msg)
345 ULONGLONG start_tick, cal_ticks;
347 int cal_length=100000;
350 start_tick = PERFGetTicks();
351 for(i=0; i<cal_length; i++)
353 PERFCalibrationFunction();
355 cal_ticks = PERFGetTicks() - start_tick;
356 printf("%s: %g\n", msg, (double)(cal_ticks/cal_length));
361 PERFInitialize(LPWSTR command_line, LPWSTR exe_path)
366 // Check if PAL Perf should be disabled
367 char *pal_perf_tracing_env = MiscGetenv(PAL_PERF_TRACING);
368 if ( pal_perf_tracing_env == NULL || strlen(pal_perf_tracing_env) == 0)
370 pal_perf_enabled = FALSE;
375 pal_perf_enabled = TRUE;
377 if (!PERFInitProgramInfo(command_line, exe_path))
380 pal_profile_on = FALSE; // turn it off until we setup everything.
381 // allocate the TLS index for structures
382 #ifndef PLATFORM_UNIX
383 if( ( PERF_tlsTableKey = TlsAlloc() ) == -1 )
386 if( pthread_key_create(&PERF_tlsTableKey , NULL) != 0 )
392 pal_function_map = (char*)PAL_malloc(PAL_API_NUMBER);
393 if(pal_function_map != NULL)
395 bRead = PERFReadSetting( ); // we don't quit even we failed to read the file.
398 /* free the index in TLS */
402 #ifndef PLATFORM_UNIX
403 TlsFree(PERF_tlsTableKey );
405 pthread_key_delete(PERF_tlsTableKey );
411 PERFCalibrate("Overhead when profiling is disabled process-wide");
417 void PERFTerminate( )
419 static LONG pal_perf_terminated = FALSE;
421 if (!pal_perf_enabled || wait_for_startup)
424 // make sure PERFTerminate is called only once
425 if (InterlockedCompareExchange(&pal_perf_terminated, TRUE, FALSE))
429 #ifndef PLATFORM_UNIX
430 TlsFree(PERF_tlsTableKey );
432 pthread_key_delete(PERF_tlsTableKey );
434 PAL_free(pal_function_map);
438 BOOL PERFAllocThreadInfo( )
440 pal_perf_api_info * apiTable = NULL;
441 pal_thread_list_node * node = NULL;
442 pal_perf_thread_info * local_info = NULL;
443 char * log_buf = NULL;
447 if (!pal_perf_enabled)
450 /* The memory allocated per thread for PAL perf tracing is never freed until PAL_Terminate
451 is called in the current implementation. If the test program keeps creating new threads,
452 memory resources could be exhausted. If this ever becomes a problem, the memory allocated
453 per thread should be freed when a thread exits. */
455 node = ( pal_thread_list_node * )PAL_malloc(sizeof(pal_thread_list_node));
459 goto PERFAllocThreadInfoExit;
462 local_info = (pal_perf_thread_info *)PAL_malloc(sizeof(pal_perf_thread_info));
463 if (local_info == NULL)
466 goto PERFAllocThreadInfoExit;
469 apiTable = (pal_perf_api_info *)PAL_malloc( PAL_API_NUMBER * sizeof(pal_perf_api_info));
470 if (apiTable == NULL)
473 goto PERFAllocThreadInfoExit;
476 node->thread_info = local_info;
477 local_info->api_table=apiTable;
478 local_info->threadId = THREADSilentGetCurrentThreadId();
480 for (i = 0; i < PAL_API_NUMBER; i++)
482 apiTable[i].entries = 0;
483 apiTable[i].counter = 0;
484 apiTable[i].min_duration = _UI64_MAX;
485 apiTable[i].max_duration = 0;
486 apiTable[i].sum_duration = 0;
487 apiTable[i].sum_of_square_duration = 0.0;
488 if (pal_perf_histogram_size > 0)
490 apiTable[i].histograms = (DWORD *)PAL_malloc(pal_perf_histogram_size*sizeof(DWORD));
491 if (apiTable[i].histograms == NULL)
494 goto PERFAllocThreadInfoExit;
496 memset(apiTable[i].histograms, 0, pal_perf_histogram_size*sizeof(DWORD));
500 apiTable[i].histograms = NULL;
504 log_buf = (char * )PAL_malloc( PAL_PERF_PROFILE_BUFFER_SIZE );
509 goto PERFAllocThreadInfoExit;
512 local_info->pal_write_buf=log_buf;
513 local_info->buf_offset = 0;
514 local_info->profile_enabled = FALSE;
515 local_info->total_duration = 0;
516 local_info->start_ticks = 0;
517 memset(log_buf, 0, PAL_PERF_PROFILE_BUFFER_SIZE);
519 #ifndef PLATFORM_UNIX
520 if ( TlsSetValue(PERF_tlsTableKey, local_info) == 0)
523 if (pthread_setspecific(PERF_tlsTableKey, local_info) != 0)
527 PERFAllocThreadInfoExit:
530 node->next = process_pal_thread_list;
531 process_pal_thread_list = node;
532 PERFFlushLog(local_info, TRUE);
540 if (local_info != NULL)
542 PAL_free(local_info);
544 if (apiTable != NULL)
546 for (i = 0; i < PAL_API_NUMBER; i++)
548 if (apiTable[i].histograms != NULL)
550 PAL_free(apiTable[i].histograms);
565 PERFUpdateProgramInfo(pal_perf_thread_info* local_info)
569 if (!local_info) return;
571 // add the elapsed time to the program's total
572 if (local_info->total_duration == 0)
574 // this thread did not go through PERFDisableThreadProfile code
575 // so compute the total elapsed time for the thread here
576 local_info->total_duration = PERFGetTicks() - local_info->start_ticks;
578 program_info.total_duration += local_info->total_duration;
580 // Add up all the time spent in PAL
581 if (local_info->api_table) {
582 for(i=0; i<PAL_API_NUMBER; i++) {
583 program_info.pal_duration += local_info->api_table[i].sum_duration;
593 pal_thread_list_node * current, * node;
594 pal_perf_api_info * table1, *table0;
596 node = process_pal_thread_list;
597 if(node == NULL || node->thread_info == NULL || node->thread_info->api_table == NULL ) // should not come here
601 process_pal_thread_list = process_pal_thread_list->next;
602 table0 = node->thread_info->api_table;
604 PERFUpdateProgramInfo(node->thread_info);
606 while(process_pal_thread_list)
608 current=process_pal_thread_list;
609 process_pal_thread_list = process_pal_thread_list->next;
610 if (current->thread_info)
612 if (current->thread_info->api_table)
614 table1 = current->thread_info->api_table;
615 for(i=0;i<PAL_API_NUMBER;i++)
618 if (table1[i].counter == 0)
622 for (j = 0; j < pal_perf_histogram_size; j++)
624 table0[i].histograms[j] += table1[i].histograms[j];
626 table0[i].entries += table1[i].entries;
627 table0[i].counter += table1[i].counter;
628 if (table0[i].min_duration > table1[i].min_duration)
629 table0[i].min_duration = table1[i].min_duration;
630 if (table0[i].max_duration < table1[i].max_duration)
631 table0[i].max_duration = table1[i].max_duration;
632 table0[i].sum_duration += table1[i].sum_duration;
633 table0[i].sum_of_square_duration += table1[i].sum_of_square_duration;
635 PERFUpdateProgramInfo(current->thread_info);
636 if (table1->histograms != NULL)
638 PAL_free(table1->histograms);
642 PERFFlushLog(current->thread_info, FALSE);
643 PAL_free(current->thread_info->pal_write_buf);
644 PAL_free(current->thread_info);
648 PERFWriteCounters(table0);
649 if (table0->histograms != NULL)
651 PAL_free(table0->histograms);
654 PERFFlushLog(node->thread_info, FALSE);
655 PAL_free(node->thread_info->pal_write_buf);
656 PAL_free(node->thread_info);
662 PERFLogFileName(PathCharString& destFileString, const char *fileName, const char *suffix)
664 const char *dir_path;
665 CPalThread* pThread = InternalGetCurrentThread();
666 dir_path = (profile_log_path == NULL) ? "." : profile_log_path;
668 destFileString.Append(dir_path, strlen(dir_path));
669 destFileString.Append(PATH_SEPARATOR, strlen(PATH_SEPARATOR));
670 if (fileName != NULL)
672 destFileString.Append(fileName, strlen(fileName));
677 char* process_id = itoa(program_info.process_id, buffer, 10);
678 destFileString.Append(process_id, strlen(process_id));
679 destFileString.Append("_", 1);
681 char* current_thread = itoa(THREADSilentGetCurrentThreadId(),buffer, 10);
682 destFileString.Append(current_thread, strlen( current_thread));
683 destFileString.Append(suffix, strlen(suffix));
690 PERFWriteCounters( pal_perf_api_info * table )
692 PathCharString fileName;
693 pal_perf_api_info * off;
699 PERFLogFileName(fileName, profile_summary_log_name, "_perf_summary.log");
700 hFile = PERF_FILEFN(fopen)(fileName, "a+");
703 PERFPrintProgramHeaderInfo(hFile, TRUE);
704 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");
705 for(i=0;i<PAL_API_NUMBER;i++)
708 ULONGLONG min_duration;
710 min_duration = (off->min_duration == _UI64_MAX) ? 0 : off->min_duration;
711 if (off->counter >= 1)
713 dev = PERFComputeStandardDeviation(off);
720 if (off->counter > 0 || !report_only_called_apis)
722 PERF_FILEFN(fprintf)(hFile,"%s\t%d\t" LLFORMAT "\t" LLFORMAT "\t" LLFORMAT "\t" LLFORMAT "\t" LLFORMAT "\t%g\t%g\n",
723 API_list[i], i, off->entries, off->counter,off->sum_duration,
724 min_duration, off->max_duration, dev, off->sum_of_square_duration);
734 PERF_FILEFN(fclose)(hFile);
736 if (pal_perf_histogram_size > 0)
739 PERFLogFileName(fileName, profile_summary_log_name, "_perf_summary.hist");
740 hFile = PERF_FILEFN(fopen)(fileName, "a+");
745 PERF_FILEFN(fprintf)(hFile,"#api_name\tapi_id");
746 for (j = 0; j < pal_perf_histogram_size; j++)
748 PERF_FILEFN(fprintf)(hFile, "\t%d", j*pal_perf_histogram_step);
750 PERF_FILEFN(fprintf)(hFile, "\n");
752 for(i = 0; i < PAL_API_NUMBER; i++)
754 if (off->counter > 0)
756 PERF_FILEFN(fprintf)(hFile,"%s\t%d", API_list[i], i);
758 for (j = 0; j < pal_perf_histogram_size; j++)
760 PERF_FILEFN(fprintf)(hFile, "\t%d", off->histograms[j]);
763 PERF_FILEFN(fprintf)(hFile, "\n");
773 PERF_FILEFN(fclose)(hFile);
783 // this function is not safe right now.
784 //more code is required to deal with corrupted input file.
787 char line[PAL_PERF_MAX_INPUT];
789 char function_name[PAL_PERF_MAX_FUNCTION_NAME]; //no function can be longer than 127 bytes.
791 char * file_name_buf;
792 PathCharString file_name_bufPS;
793 char * input_file_name;
794 char * summary_flag_env;
795 char * nested_tracing_env;
796 char * calibrate_env;
797 char * report_only_called_apis_env;
798 char * wait_for_startup_env;
799 char * pal_perf_histogram_size_env;
800 char * pal_perf_histogram_step_env;
808 if((pal_function_map == NULL) || (PAL_API_NUMBER < 0) )
810 // should not be here.
813 /* do some env setting here */
814 summary_flag_env = MiscGetenv(PAL_SUMMARY_FLAG);
815 if (summary_flag_env == NULL || strlen(summary_flag_env) == 0)
817 summary_only = FALSE;
823 nested_tracing_env = MiscGetenv(PAL_PERF_NESTED_TRACING);
824 if (nested_tracing_env == NULL || strlen(nested_tracing_env) == 0)
826 nested_tracing = FALSE;
830 nested_tracing = TRUE;
833 calibrate_env = MiscGetenv(PAL_PERF_CALIBRATE);
834 if (calibrate_env == NULL || strlen(calibrate_env) == 0)
843 report_only_called_apis_env = MiscGetenv(PAL_PERF_REPORT_ONLY_CALLED_APIS);
844 if (report_only_called_apis_env == NULL || strlen(report_only_called_apis_env) == 0)
846 report_only_called_apis = FALSE;
850 report_only_called_apis = TRUE;
853 wait_for_startup_env = MiscGetenv(PAL_PERF_WAIT_FOR_STARTUP);
854 if (wait_for_startup_env == NULL || strlen(wait_for_startup_env) == 0)
856 wait_for_startup = FALSE;
860 wait_for_startup = TRUE;
863 pal_perf_histogram_size_env = MiscGetenv(PAL_PERF_HISTOGRAM_SIZE);
864 if (pal_perf_histogram_size_env != NULL && strlen(pal_perf_histogram_size_env) > 0)
868 value = strtol(pal_perf_histogram_size_env, &endptr, 10);
871 pal_perf_histogram_size = (DWORD) value;
875 pal_perf_histogram_step_env = MiscGetenv(PAL_PERF_HISTOGRAM_STEP);
876 if (pal_perf_histogram_step_env != NULL && strlen(pal_perf_histogram_step_env) > 0)
880 value = strtol(pal_perf_histogram_step_env, &endptr, 10);
883 pal_perf_histogram_step = (DWORD) value;
887 traced_apis_file = PERFIsValidFile("", MiscGetenv(PAL_PERF_TRACEDAPIS_PATH));
888 enabledapis_path = PERFIsValidFile("", MiscGetenv(PAL_PERF_ENABLED_APIS_PATH));
889 profile_log_path = PERFIsValidPath(MiscGetenv(PAL_PERF_LOG_PATH));
890 perf_default_path = PERFIsValidPath( MiscGetenv(PAL_DEFAULT_PATH));
891 profile_summary_log_name = MiscGetenv(PAL_PERF_SUMMARY_LOG_NAME);
892 if (profile_summary_log_name != NULL && strlen(profile_summary_log_name) == 0)
893 profile_summary_log_name = NULL;
894 profile_time_log_name = MiscGetenv(PAL_PERF_TIME_LOG_NAME);
895 if (profile_time_log_name != NULL && strlen(profile_time_log_name) == 0)
896 profile_time_log_name = NULL;
898 if( traced_apis_file == NULL)
900 if(perf_default_path==NULL)
903 input_file_name = NULL;
907 if( PERFIsValidFile(perf_default_path,traced_apis_filename))
909 int length = strlen(perf_default_path) + strlen(PATH_SEPARATOR) + strlen(traced_apis_filename);
910 file_name_buf = file_name_bufPS.OpenStringBuffer(length);
911 if ((strcpy_s(file_name_buf, file_name_bufPS.GetSizeOf(), perf_default_path) != SAFECRT_SUCCESS) ||
912 (strcat_s(file_name_buf, file_name_bufPS.GetSizeOf(), PATH_SEPARATOR) != SAFECRT_SUCCESS) ||
913 (strcat_s(file_name_buf, file_name_bufPS.GetSizeOf(), traced_apis_filename) != SAFECRT_SUCCESS))
915 file_name_bufPS.CloseBuffer(0);
917 input_file_name = NULL;
921 file_name_bufPS.CloseBuffer(length);
922 input_file_name = file_name_buf;
928 input_file_name=NULL;
934 input_file_name=traced_apis_file;
940 hFile = PAL_fopen(input_file_name, "r+");
942 hFile = fopen(input_file_name, "r+");
946 memset(pal_function_map, 1, PAL_API_NUMBER);
951 memset(pal_function_map, 0, PAL_API_NUMBER);
953 PAL_fseek(hFile, 0L, SEEK_SET);
955 /* Read a line of data from file: */
956 while ( PAL_fgets(line, PAL_PERF_MAX_INPUT, hFile) != NULL )
960 ptr = strchr( line, '#');
963 sscanf_s(line, "%s %u", function_name,&index);
965 if( index >= PAL_API_NUMBER)
967 // some code here to deal with incorrect index.
968 // use function name to cover it.
970 else if(pal_function_map[index]==1)
972 // some code here to deal with conflict index.
973 // use function name to cover it.
977 pal_function_map[index]=1;
992 memset(pal_function_map, 1, PAL_API_NUMBER);
996 if( enabledapis_path == NULL)
998 if(perf_default_path==NULL)
1000 input_file_name = NULL;
1004 if( PERFIsValidFile(perf_default_path,perf_enabled_filename))
1006 if ((strcpy_s(file_name_buf, sizeof(file_name_buf), perf_default_path) != SAFECRT_SUCCESS) ||
1007 (strcat_s(file_name_buf, sizeof(file_name_buf), PATH_SEPARATOR) != SAFECRT_SUCCESS) ||
1008 (strcat_s(file_name_buf, sizeof(file_name_buf), perf_enabled_filename) != SAFECRT_SUCCESS))
1011 input_file_name = NULL;
1015 input_file_name = file_name_buf;
1020 input_file_name=NULL;
1026 input_file_name=enabledapis_path;
1029 if(input_file_name == NULL)
1034 #ifdef PLATFORM_UNIX
1035 hFile = PAL_fopen(input_file_name, "r+");
1037 hFile = fopen(input_file_name, "r+");
1040 if ( hFile != NULL )
1042 PAL_fseek(hFile, 0L, SEEK_SET);
1044 /* Read a line of data from file: */
1045 while (PAL_fgets(line, PAL_PERF_MAX_INPUT, hFile) != NULL)
1049 ptr = strchr( line, '#');
1052 sscanf_s(line, "%s %u", function_name,&index);
1054 if( index >= PAL_API_NUMBER)
1056 // some code here to deal with incorrect index.
1057 // use function name to cover it.
1061 if (strcpy_s(API_list[index], sizeof(API_list[index]), function_name) != SAFECRT_SUCCESS)
1068 #ifdef PLATFORM_UNIX
1082 PERFFlushLog(pal_perf_thread_info * local_info, BOOL output_header)
1085 PathCharString fileName;
1086 int nWrittenBytes = 0;
1092 PERFLogFileName(fileName, profile_time_log_name, "_perf_time.log");
1094 hFile = PERF_FILEFN(fopen)(fileName, "a+");
1100 PERFPrintProgramHeaderInfo(hFile, FALSE);
1102 if (local_info->buf_offset > 0)
1104 nWrittenBytes = PERF_FILEFN(fwrite)(local_info->pal_write_buf, local_info->buf_offset, 1, hFile);
1105 if (nWrittenBytes < 1)
1107 ERROR("fwrite() failed with errno == %d\n", errno);
1110 local_info->buf_offset = 0;
1112 PERF_FILEFN(fclose)(hFile);
1120 PERFLogFunctionEntry(unsigned int pal_api_id, ULONGLONG *pal_perf_start_tick )
1122 pal_perf_thread_info * local_info=NULL;
1123 pal_perf_api_info * table;
1129 #ifndef PLATFORM_UNIX
1132 last_error = GetLastError();
1138 if(!pal_perf_enabled || pal_function_map==NULL || !pal_profile_on ) // haven't initialize, just quit.
1141 if( pal_function_map[pal_api_id] )
1143 local_info= (pal_perf_thread_info * )pthread_getspecific(PERF_tlsTableKey);
1145 if (local_info==NULL )
1149 if ( !local_info->profile_enabled ) /* prevent recursion. */
1153 // turn on this flag before call any other functions
1154 local_info->profile_enabled = FALSE;
1155 table = local_info->api_table;
1156 table[pal_api_id].entries++;
1160 write_buf = (local_info->pal_write_buf);
1161 if(local_info->buf_offset >= PAL_PERF_BUFFER_FULL)
1163 PERFFlushLog(local_info, FALSE);
1166 #ifndef PLATFORM_UNIX
1167 tv = GetTickCount();
1169 gettimeofday(&tv, NULL);
1172 buf_off = local_info->buf_offset;
1174 #ifndef PLATFORM_UNIX
1175 bufused = snprintf(&write_buf[buf_off], PAL_PERF_MAX_LOGLINE, "----> %d %lu entry.\n", pal_api_id, tv );
1177 bufused = snprintf(&write_buf[buf_off], PAL_PERF_MAX_LOGLINE, "----> %d %lu %06u entry.\n", pal_api_id, tv.tv_sec, tv.tv_usec );
1179 local_info->buf_offset += bufused;
1182 local_info->profile_enabled = TRUE;
1183 *pal_perf_start_tick = PERFGetTicks();
1185 #ifndef PLATFORM_UNIX
1186 SetLastError( last_error );
1193 PERFUpdateApiInfo(pal_perf_api_info *api, ULONGLONG duration)
1198 if (api->min_duration > duration)
1199 api->min_duration = duration;
1200 if (api->max_duration < duration)
1201 api->max_duration = duration;
1202 api->sum_duration += duration;
1203 api->sum_of_square_duration += (double) duration * (double)duration;
1205 if (pal_perf_histogram_size > 0)
1207 iBucket = (DWORD)(duration / pal_perf_histogram_step);
1208 if (iBucket >= pal_perf_histogram_size)
1210 iBucket = pal_perf_histogram_size - 1;
1212 api->histograms[iBucket]++;
1218 PERFLogFunctionExit(unsigned int pal_api_id, ULONGLONG *pal_perf_start_tick )
1221 pal_perf_thread_info * local_info;
1225 ULONGLONG duration = 0;
1226 #ifndef PLATFORM_UNIX
1229 last_error = GetLastError();
1231 struct timeval timev;
1235 if(!pal_perf_enabled || (pal_function_map == NULL) || !pal_profile_on ) // haven't initiallize yet, just quit.
1238 if (*pal_perf_start_tick != 0)
1240 duration = PERFGetTicks() - *pal_perf_start_tick;
1244 return; // pal_perf_start_tick == 0 indicates that we exited PERFLogFunctionEntry before getting the ticks.
1247 if( pal_function_map[pal_api_id] )
1249 local_info = (pal_perf_thread_info*)pthread_getspecific(PERF_tlsTableKey);
1251 if (NULL == local_info ){
1254 PERFUpdateApiInfo(&local_info->api_table[pal_api_id], duration);
1255 *pal_perf_start_tick = 0;
1259 local_info->profile_enabled = TRUE;
1260 #ifndef PLATFORM_UNIX
1261 SetLastError( last_error );
1266 #ifndef PLATFORM_UNIX
1267 timev = GetTickCount();
1269 gettimeofday(&timev, NULL);
1272 buf = local_info->pal_write_buf;
1273 if(local_info->buf_offset >= PAL_PERF_BUFFER_FULL)
1275 PERFFlushLog(local_info, FALSE);
1277 off = local_info->buf_offset;
1279 #ifndef PLATFORM_UNIX
1280 bufused = snprintf(&buf[off], PAL_PERF_MAX_LOGLINE, "<---- %d %lu exit. \n", pal_api_id, timev);
1282 bufused = snprintf(&buf[off], PAL_PERF_MAX_LOGLINE, "<---- %d %lu %06u exit. \n", pal_api_id, timev.tv_sec, timev.tv_usec );
1284 local_info->buf_offset += bufused;
1285 local_info->profile_enabled = TRUE;
1287 #ifndef PLATFORM_UNIX
1288 SetLastError( last_error );
1294 PERFNoLatencyProfileEntry(unsigned int pal_api_id )
1296 pal_perf_thread_info * local_info=NULL;
1297 pal_perf_api_info * table;
1298 #ifndef PLATFORM_UNIX
1300 last_error = GetLastError();
1303 if(!pal_perf_enabled || pal_function_map==NULL || !pal_profile_on ) // haven't initialize, just quit.
1305 if( pal_function_map[pal_api_id] )
1307 local_info= (pal_perf_thread_info * )pthread_getspecific(PERF_tlsTableKey);
1308 if (local_info==NULL )
1310 #ifndef PLATFORM_UNIX
1311 SetLastError( last_error );
1316 table = local_info->api_table;
1317 table[pal_api_id].entries++;
1320 #ifndef PLATFORM_UNIX
1321 SetLastError( last_error );
1328 PERFEnableThreadProfile(BOOL isInternal)
1330 pal_perf_thread_info * local_info;
1331 #ifndef PLATFORM_UNIX
1333 last_error = GetLastError();
1335 if (!pal_perf_enabled)
1337 if (NULL != (local_info = (pal_perf_thread_info*)pthread_getspecific(PERF_tlsTableKey)))
1339 if (!isInternal || nested_tracing) {
1340 local_info->profile_enabled = TRUE;
1341 local_info->start_ticks = PERFGetTicks();
1344 #ifndef PLATFORM_UNIX
1345 SetLastError( last_error );
1351 PERFDisableThreadProfile(BOOL isInternal)
1353 pal_perf_thread_info * local_info;
1354 #ifndef PLATFORM_UNIX
1356 last_error = GetLastError();
1358 if (!pal_perf_enabled)
1360 if (NULL != (local_info = (pal_perf_thread_info*)pthread_getspecific(PERF_tlsTableKey)))
1362 if (!isInternal || nested_tracing) {
1363 local_info->profile_enabled = FALSE;
1364 local_info->total_duration = PERFGetTicks() - local_info->start_ticks;
1367 #ifndef PLATFORM_UNIX
1368 SetLastError( last_error );
1374 PERFEnableProcessProfile( )
1376 if (!pal_perf_enabled || wait_for_startup)
1378 pal_profile_on = TRUE;
1379 PERFCalibrate("Overhead when profiling is disabled temporarily for a thread");
1380 // record the cpu clock ticks at the beginning of the profiling.
1381 program_info.start_ticks = PERFGetTicks();
1386 PERFDisableProcessProfile( )
1388 if (!pal_perf_enabled)
1390 pal_profile_on = FALSE;
1391 // compute the total program duration in cpu clock ticks.
1392 if (program_info.start_ticks != 0)
1394 program_info.elapsed_time += (PERFGetTicks() - program_info.start_ticks);
1395 program_info.start_ticks = 0;
1400 PERFIsProcessProfileEnabled( )
1402 return pal_profile_on;
1407 PERFIsValidPath( const char * path )
1409 #ifndef PLATFORM_UNIX
1415 if(( path==NULL) || (strlen(path)==0))
1418 #ifndef PLATFORM_UNIX
1419 result = GetFileAttributesA( path );
1420 if ((result != INVALID_FILE_ATTRIBUTES) && (result & FILE_ATTRIBUTE_DIRECTORY))
1422 return ((char *) path );
1425 dir = opendir(path);
1429 return ((char *)path);
1437 PERFIsValidFile( const char * path, const char * file)
1441 PathCharString tempPS;
1443 if(file==NULL || strlen(file)==0)
1446 if ( strcmp(path, "") )
1448 int length = strlen(path) + strlen(PATH_SEPARATOR) + strlen(file);
1449 temp = tempPS.OpenStringBuffer(length);
1450 if ((strcpy_s(temp, sizeof(temp), path) != SAFECRT_SUCCESS) ||
1451 (strcat_s(temp, sizeof(temp), PATH_SEPARATOR) != SAFECRT_SUCCESS) ||
1452 (strcat_s(temp, sizeof(temp), file) != SAFECRT_SUCCESS))
1454 tempPS.CloseBuffer(0);
1458 tempPS.CloseBuffer(length);
1459 hFile = fopen(temp, "r");
1463 hFile = fopen(file, "r");
1469 return ((char *) file);
1479 PAL_EnableProcessProfile(VOID)
1481 wait_for_startup = FALSE;
1482 pal_profile_on = TRUE;
1483 PERFEnableProcessProfile();
1489 PAL_DisableProcessProfile(VOID)
1491 pal_profile_on = FALSE;
1492 PERFDisableProcessProfile();
1498 PAL_IsProcessProfileEnabled(VOID)
1500 return PERFIsProcessProfileEnabled();
1506 PAL_GetCpuTickCount(VOID)
1508 return PERFGetTicks();
1511 #ifndef PLATFORM_UNIX
1514 #undef pthread_key_t
1515 #undef pthread_getspecific
1516 #endif /* ifndef PLATFORM_UNIX definitions */
1518 #endif /* PAL_PERF */