d4fba3367a6f60eecfbc2f672a036042bf29ccd7
[platform/upstream/coreclr.git] / src / pal / src / misc / perftrace.cpp
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.
4
5 /*++
6
7
8
9 Module Name:
10
11     misc/perftrace.c
12
13 Abstract:
14     Implementation of PAL Performance trace utilities.
15
16
17
18 --*/
19
20 /* PAL headers */
21
22
23
24 #ifdef PAL_PERF
25
26 #ifndef PLATFORM_UNIX
27 /* PAL Headers */
28 #include "perftrace.h"
29
30 /* Standard Headers */
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <errno.h>
35
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.
44
45 #else
46 /* PAL Headers */
47 #include "pal/palinternal.h"
48 #include "pal/perftrace.h"
49 #include "pal/dbgmsg.h"
50 #include "pal/cruntime.h"
51 #include "pal/misc.h"
52
53 /* Standard headers */
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <errno.h>
58 #include <pthread.h> /* for pthread_self */
59 #include <dirent.h>
60 #include <unistd.h>
61
62 SET_DEFAULT_DEBUG_CHANNEL(MISC);
63 #endif  //End of PLATFORM_UNIX
64
65
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) */
71
72 typedef struct _pal_perf_api_info
73 {
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. */
81 } pal_perf_api_info;
82
83
84 typedef struct _pal_perf_thread_info
85 {
86     DWORD               threadId; 
87     pal_perf_api_info * api_table;
88     char *              pal_write_buf;
89     DWORD               buf_offset;
90     BOOL                profile_enabled;
91     ULONGLONG           start_ticks; 
92     ULONGLONG           total_duration; 
93 } pal_perf_thread_info;
94
95 typedef struct _pal_thread_list_node
96 {
97     pal_perf_thread_info * thread_info;  
98     struct _pal_thread_list_node * next;
99
100 } pal_thread_list_node;
101
102 typedef struct _pal_perf_program_info
103 {
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 */
112
113 #ifndef PLATFORM_UNIX
114     DWORD   process_id;
115 #else
116     pid_t   process_id;
117 #endif
118     char    start_time[32]; /*  must be at least 26 characters */
119 } pal_perf_program_info;
120
121 #ifndef PLATFORM_UNIX
122 typedef FILE PERF_FILE;
123 #define PERF_FILEFN(x) x
124 #else
125 typedef PAL_FILE PERF_FILE;
126 #define PERF_FILEFN(x) PAL_ ## x
127 #endif
128
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);
141
142 typedef char PAL_API_NAME[PAL_PERF_MAX_FUNCTION_NAME];
143
144 static PAL_API_NAME API_list[PAL_API_NUMBER] ;
145 static pal_perf_program_info program_info;
146
147 #ifndef PLATFORM_UNIX
148 static DWORD PERF_tlsTableKey=0 ;
149 #else
150 static pthread_key_t PERF_tlsTableKey=0 ;
151 #endif
152
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;
166
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;
171
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;
176
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;
181
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;
185
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[] = "\\";
204 #else
205 static const char PATH_SEPARATOR[] = "/";
206 #endif
207
208
209
210 #ifndef PLATFORM_UNIX
211 #define LLFORMAT "%I64u"
212 #else
213 #define LLFORMAT "%llu"
214 #endif
215
216 static
217 ULONGLONG
218 PERFGetTicks(){
219 #ifdef _X86_ // for BSD and Windows.
220     unsigned long a, d;
221   #ifdef _MSC_VER
222   __asm{
223             rdtsc
224             mov a, eax
225             mov d, edx
226        }
227   #else
228   #undef volatile
229   asm volatile("rdtsc":"=a" (a), "=d" (d));
230   #define volatile DoNotUseVolatileKeyword
231   #endif
232   return ((ULONGLONG)((unsigned int)(d)) << 32) | (unsigned int)(a);
233 #else
234 #ifdef __sparc__
235   return (ULONGLONG)gethrtime();
236 #else
237   return 0; // on non-BSD and non-Windows, we'll return 0 for now.
238 #endif // __sparc__
239 #endif // _X86_
240 }
241
242 static
243 double
244 PERFComputeStandardDeviation(pal_perf_api_info *api)
245 {
246     double n;
247     double sum_of_variance;
248     if (api->counter <= 1)
249         return 0.0;
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)
255         return 0.0;
256     return sqrt(sum_of_variance/(n*n));
257 }
258
259
260 static
261 void
262 PERFPrintProgramHeaderInfo(PERF_FILE * hFile, BOOL completedExecution)
263 {
264     ULONGLONG etime = 0;
265     ULONGLONG ttime = 0;
266     ULONGLONG ptime = 0;
267     if (completedExecution) {
268        etime = program_info.elapsed_time;
269        ttime = program_info.total_duration;
270        ptime = program_info.pal_duration;
271     }
272     PERF_FILEFN(fprintf)(hFile,"#LOG\tversion=1.00\n");
273
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);
279 }
280
281 static
282 BOOL
283 PERFInitProgramInfo(LPWSTR command_line, LPWSTR exe_path)
284 {
285     ULONGLONG start_tick;
286 #ifndef PLATFORM_UNIX
287     time_t tv;
288 #else
289     struct timeval tv;
290 #endif
291
292     if (WideCharToMultiByte(CP_ACP, 0, command_line, -1, 
293                         program_info.command_line, PAL_PERF_MAX_LOGLINE-1, NULL, NULL) == 0)
294         return FALSE;
295     if (WideCharToMultiByte(CP_ACP, 0, exe_path, -1, 
296                         program_info.exe_path, PAL_PERF_MAX_LOGLINE-1, NULL, NULL) == 0)
297         return FALSE;
298
299     gethostname(program_info.hostname, PAL_PERF_MAX_FUNCTION_NAME);
300     program_info.process_id = getpid();
301
302 #ifndef PLATFORM_UNIX
303     time( &tv );
304     strcpy(program_info.start_time, ctime( &tv ));
305 #else
306     gettimeofday(&tv, NULL);
307     ctime_r(&tv.tv_sec, program_info.start_time);
308 #endif
309
310     // estimate the cpu clock cycles
311     start_tick = PERFGetTicks();
312     if (start_tick != 0)
313     {
314 #ifndef PLATFORM_UNIX
315         Sleep(1000); //Sleep on Windows takes milliseconds as argument
316 #else
317         sleep(1);
318 #endif
319         program_info.cpu_clock_frequency = (double) (PERFGetTicks() - start_tick);
320     }
321     else
322     {
323         program_info.cpu_clock_frequency = 0.0;
324     }
325
326     program_info.start_ticks = 0;
327     program_info.elapsed_time = 0;
328     program_info.total_duration = 0;
329     program_info.pal_duration = 0;
330
331     return TRUE;
332 }
333
334 static
335 void
336 PERFCalibrationFunction()
337 {
338     PERF_ENTRY(CalibrationFunction);
339     PERF_EXIT(CalibrationFunction);
340 }
341
342 void
343 PERFCalibrate(const char* msg)
344 {
345     ULONGLONG start_tick, cal_ticks;
346     int i=0;
347     int cal_length=100000;
348
349     if (calibrate) {
350        start_tick = PERFGetTicks();
351        for(i=0; i<cal_length; i++)
352        {
353           PERFCalibrationFunction();
354        }
355        cal_ticks = PERFGetTicks() - start_tick;
356        printf("%s: %g\n", msg, (double)(cal_ticks/cal_length));
357     }
358 }
359
360 BOOL
361 PERFInitialize(LPWSTR command_line, LPWSTR exe_path)
362 {
363     BOOL bRead;
364     BOOL ret = TRUE;
365
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)
369     {
370         pal_perf_enabled = FALSE;
371         return TRUE;
372     }
373     else
374     {
375         pal_perf_enabled = TRUE;
376     }
377     if (!PERFInitProgramInfo(command_line, exe_path))
378         return FALSE;
379
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 )
384         ret = FALSE;
385 #else
386     if( pthread_key_create(&PERF_tlsTableKey , NULL) != 0 )
387        ret = FALSE;
388 #endif
389
390     if( ret == TRUE )
391     {
392         pal_function_map = (char*)PAL_malloc(PAL_API_NUMBER);
393         if(pal_function_map != NULL)
394         {  
395             bRead = PERFReadSetting( );  // we don't quit even we failed to read the file.
396             ret = TRUE;
397         }
398         /* free the index in TLS */
399         else
400         {
401
402 #ifndef PLATFORM_UNIX
403             TlsFree(PERF_tlsTableKey );
404 #else
405             pthread_key_delete(PERF_tlsTableKey );
406 #endif
407             ret = FALSE;
408         }
409     }
410
411     PERFCalibrate("Overhead when profiling is disabled process-wide");
412
413     return ret;
414 }
415
416
417 void PERFTerminate(  )
418 {
419     static LONG pal_perf_terminated = FALSE;
420
421     if (!pal_perf_enabled || wait_for_startup)
422         return;
423
424     // make sure PERFTerminate is called only once
425     if (InterlockedCompareExchange(&pal_perf_terminated, TRUE, FALSE))
426         return;
427
428     PERFlushAllLogs();
429 #ifndef PLATFORM_UNIX
430     TlsFree(PERF_tlsTableKey );
431 #else
432         pthread_key_delete(PERF_tlsTableKey );
433 #endif
434     PAL_free(pal_function_map);
435 }
436
437
438 BOOL PERFAllocThreadInfo(  )
439 {
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;
444     int i;
445     BOOL ret = TRUE;
446
447     if (!pal_perf_enabled)
448         return TRUE;
449
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. */
454
455     node = ( pal_thread_list_node * )PAL_malloc(sizeof(pal_thread_list_node));
456     if(node == NULL)
457     {
458         ret = FALSE;
459         goto PERFAllocThreadInfoExit;
460     }
461
462     local_info = (pal_perf_thread_info *)PAL_malloc(sizeof(pal_perf_thread_info)); 
463     if (local_info == NULL)
464     {
465         ret = FALSE;
466         goto PERFAllocThreadInfoExit;
467     }
468
469     apiTable = (pal_perf_api_info *)PAL_malloc( PAL_API_NUMBER *  sizeof(pal_perf_api_info));
470     if (apiTable == NULL)
471     {
472         ret = FALSE;
473         goto PERFAllocThreadInfoExit;
474     }
475
476     node->thread_info = local_info;
477     local_info->api_table=apiTable;
478     local_info->threadId = THREADSilentGetCurrentThreadId();
479
480     for (i = 0; i < PAL_API_NUMBER; i++)
481     {
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)
489         {
490             apiTable[i].histograms = (DWORD *)PAL_malloc(pal_perf_histogram_size*sizeof(DWORD));
491             if (apiTable[i].histograms == NULL)
492             {
493                 ret = FALSE;
494                 goto PERFAllocThreadInfoExit;
495             }
496             memset(apiTable[i].histograms, 0, pal_perf_histogram_size*sizeof(DWORD));
497         }
498         else
499         {
500             apiTable[i].histograms = NULL;
501         }
502     }
503
504     log_buf = (char * )PAL_malloc( PAL_PERF_PROFILE_BUFFER_SIZE );
505
506     if(log_buf == NULL)
507     {
508         ret = FALSE;
509         goto PERFAllocThreadInfoExit;
510     }
511
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);
518
519 #ifndef PLATFORM_UNIX
520     if ( TlsSetValue(PERF_tlsTableKey, local_info) == 0)
521         ret = FALSE;
522 #else
523     if (pthread_setspecific(PERF_tlsTableKey, local_info) != 0)
524        ret = FALSE;
525 #endif
526
527 PERFAllocThreadInfoExit:
528     if (ret == TRUE)
529     {
530         node->next = process_pal_thread_list;
531         process_pal_thread_list = node;
532         PERFFlushLog(local_info, TRUE);
533     }
534     else
535     {
536         if (node != NULL)
537         {
538             PAL_free(node);
539         }
540         if (local_info != NULL)
541         {
542             PAL_free(local_info);
543         }
544         if (apiTable != NULL)
545         {
546             for (i = 0; i < PAL_API_NUMBER; i++)
547             {
548                 if (apiTable[i].histograms != NULL)
549                 {
550                     PAL_free(apiTable[i].histograms);
551                 }
552             }
553             PAL_free(apiTable);
554         }
555         if (log_buf != NULL)
556         {
557             PAL_free(log_buf);
558         }
559     }
560     return ret;
561 }
562
563 static
564 void
565 PERFUpdateProgramInfo(pal_perf_thread_info* local_info)
566 {
567     int i;
568
569     if (!local_info) return;
570
571     // add the elapsed time to the program's total
572     if (local_info->total_duration == 0)
573     {
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;
577     }
578     program_info.total_duration += local_info->total_duration;
579
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;
584        }
585     }
586 }
587
588
589 static
590 void
591 PERFlushAllLogs( )
592 {
593     pal_thread_list_node * current, * node;
594     pal_perf_api_info * table1, *table0;
595     int i; 
596     node = process_pal_thread_list;
597     if(node == NULL || node->thread_info == NULL || node->thread_info->api_table == NULL )   // should not come here 
598     {
599         return ;
600     }
601     process_pal_thread_list = process_pal_thread_list->next;
602     table0 = node->thread_info->api_table;
603
604     PERFUpdateProgramInfo(node->thread_info);
605
606     while(process_pal_thread_list)
607     {
608         current=process_pal_thread_list;
609         process_pal_thread_list = process_pal_thread_list->next;
610         if (current->thread_info)
611         {
612             if (current->thread_info->api_table)
613             {
614                 table1 = current->thread_info->api_table;
615                 for(i=0;i<PAL_API_NUMBER;i++)
616                 { 
617                     DWORD j;
618                     if (table1[i].counter == 0)
619                     {
620                         continue;
621                     }
622                     for (j = 0; j < pal_perf_histogram_size; j++)
623                     {
624                         table0[i].histograms[j] += table1[i].histograms[j];
625                     }
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;
634                }
635                 PERFUpdateProgramInfo(current->thread_info);
636                 if (table1->histograms != NULL)
637                 {
638                     PAL_free(table1->histograms);
639                 }
640                 PAL_free(table1);
641             }
642             PERFFlushLog(current->thread_info, FALSE);
643             PAL_free(current->thread_info->pal_write_buf);
644             PAL_free(current->thread_info);
645         }
646         PAL_free(current);
647     }
648     PERFWriteCounters(table0);
649     if (table0->histograms != NULL)
650     {
651         PAL_free(table0->histograms);
652     }
653     PAL_free(table0);
654     PERFFlushLog(node->thread_info, FALSE);
655     PAL_free(node->thread_info->pal_write_buf);
656     PAL_free(node->thread_info);
657     PAL_free(node);
658 }
659
660 static
661 void
662 PERFLogFileName(PathCharString& destFileString, const char *fileName, const char *suffix)
663 {
664     const char *dir_path;
665     CPalThread* pThread = InternalGetCurrentThread();
666     dir_path = (profile_log_path == NULL) ? "." : profile_log_path;
667         
668     destFileString.Append(dir_path, strlen(dir_path));
669     destFileString.Append(PATH_SEPARATOR, strlen(PATH_SEPARATOR));
670     if (fileName != NULL)
671     {
672         destFileString.Append(fileName, strlen(fileName));
673     }
674     else
675     {
676         char buffer[33];
677         char* process_id     = itoa(program_info.process_id, buffer, 10);
678         destFileString.Append(process_id, strlen(process_id));
679         destFileString.Append("_", 1);
680         
681         char* current_thread = itoa(THREADSilentGetCurrentThreadId(),buffer, 10);
682         destFileString.Append(current_thread, strlen( current_thread));
683         destFileString.Append(suffix, strlen(suffix));
684     }
685     
686 }
687
688 static
689 int
690 PERFWriteCounters( pal_perf_api_info * table )
691 {
692     PathCharString fileName;
693     pal_perf_api_info * off;
694     PERF_FILE * hFile;
695     int i;
696
697     off = table;
698     
699     PERFLogFileName(fileName, profile_summary_log_name, "_perf_summary.log");
700     hFile = PERF_FILEFN(fopen)(fileName, "a+");
701     if(hFile != NULL)
702     {   
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++)
706         {
707             double dev;
708             ULONGLONG min_duration;
709
710             min_duration = (off->min_duration == _UI64_MAX) ? 0 : off->min_duration;
711             if (off->counter >= 1)
712             {
713                 dev = PERFComputeStandardDeviation(off);
714             }
715             else
716             {
717                 dev = 0.0;
718             }
719
720             if (off->counter > 0 || !report_only_called_apis)
721             {
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);
725             }
726
727             off++;
728         }
729     }
730     else
731     {
732         return -1;
733     }
734     PERF_FILEFN(fclose)(hFile);
735
736     if (pal_perf_histogram_size > 0)
737     {
738         off = table;
739         PERFLogFileName(fileName, profile_summary_log_name, "_perf_summary.hist");
740         hFile = PERF_FILEFN(fopen)(fileName, "a+");
741
742         if (hFile != NULL)
743         {
744             DWORD j;
745             PERF_FILEFN(fprintf)(hFile,"#api_name\tapi_id");
746             for (j = 0; j < pal_perf_histogram_size; j++)
747             {
748                 PERF_FILEFN(fprintf)(hFile, "\t%d", j*pal_perf_histogram_step);
749             }        
750             PERF_FILEFN(fprintf)(hFile, "\n");
751
752             for(i = 0; i < PAL_API_NUMBER; i++)
753             {
754                 if (off->counter > 0)
755                 {
756                     PERF_FILEFN(fprintf)(hFile,"%s\t%d", API_list[i], i);
757                     
758                     for (j = 0; j < pal_perf_histogram_size; j++)
759                     {
760                         PERF_FILEFN(fprintf)(hFile, "\t%d", off->histograms[j]);
761                     }
762                     
763                     PERF_FILEFN(fprintf)(hFile, "\n");
764                 }
765
766                 off++;
767             }
768         }
769         else
770         {
771             return -1;
772         }
773         PERF_FILEFN(fclose)(hFile);
774     }
775
776     return 0;
777 }
778
779 static
780 BOOL 
781 PERFReadSetting(  )
782 {
783     // this function is not safe right now. 
784     //more code is required to deal with corrupted input file.
785     BOOL ret;
786     unsigned int index;
787     char line[PAL_PERF_MAX_INPUT];
788     char * ptr;
789     char function_name[PAL_PERF_MAX_FUNCTION_NAME];  //no function can be longer than 127 bytes.
790
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;
801
802 #ifdef PLATFORM_UNIX
803     PAL_FILE * hFile;
804 #else
805     FILE * hFile;
806 #endif
807
808     if((pal_function_map == NULL) || (PAL_API_NUMBER < 0) )
809     {
810         // should not be here.
811     }
812
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) 
816     {
817         summary_only = FALSE;
818     } 
819     else
820     {
821         summary_only = TRUE; 
822     }
823     nested_tracing_env = MiscGetenv(PAL_PERF_NESTED_TRACING); 
824     if (nested_tracing_env == NULL || strlen(nested_tracing_env) == 0) 
825     {
826         nested_tracing = FALSE;
827     } 
828     else
829     {
830         nested_tracing = TRUE; 
831     }
832
833     calibrate_env = MiscGetenv(PAL_PERF_CALIBRATE); 
834     if (calibrate_env == NULL || strlen(calibrate_env) == 0) 
835     {
836         calibrate = FALSE;
837     } 
838     else
839     {
840         calibrate = TRUE; 
841     }
842
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) 
845     {
846         report_only_called_apis = FALSE;
847     } 
848     else
849     {
850         report_only_called_apis = TRUE; 
851     }
852
853     wait_for_startup_env = MiscGetenv(PAL_PERF_WAIT_FOR_STARTUP); 
854     if (wait_for_startup_env == NULL || strlen(wait_for_startup_env) == 0) 
855     {
856         wait_for_startup = FALSE;
857     } 
858     else
859     {
860         wait_for_startup = TRUE; 
861     }
862
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) 
865     {
866         long value;
867         char *endptr;
868         value = strtol(pal_perf_histogram_size_env, &endptr, 10);
869         if (value > 0)
870         {
871             pal_perf_histogram_size = (DWORD) value;
872         }
873     }
874
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) 
877     {
878         long value;
879         char *endptr;
880         value = strtol(pal_perf_histogram_step_env, &endptr, 10);
881         if (value > 0)
882         {
883             pal_perf_histogram_step = (DWORD) value;
884         }
885     } 
886
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;
897
898     if( traced_apis_file == NULL)
899     {
900         if(perf_default_path==NULL)
901         { 
902             ret=FALSE;
903             input_file_name = NULL;
904         }
905         else
906         {
907             if( PERFIsValidFile(perf_default_path,traced_apis_filename))
908             {
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))
914                 {
915                     file_name_bufPS.CloseBuffer(0);
916                     ret = FALSE;
917                     input_file_name = NULL;
918                 }
919                 else
920                 {
921                     file_name_bufPS.CloseBuffer(length);
922                     input_file_name = file_name_buf;
923                 }
924             }
925             else
926             {
927                 ret = FALSE;
928                 input_file_name=NULL;
929             }
930         }
931     }
932     else
933     {
934         input_file_name=traced_apis_file;
935     }
936
937     if(input_file_name)
938     { 
939 #ifdef PLATFORM_UNIX
940         hFile = PAL_fopen(input_file_name, "r+");
941 #else
942         hFile = fopen(input_file_name, "r+");
943 #endif
944         if ( hFile == NULL )
945         {
946             memset(pal_function_map, 1, PAL_API_NUMBER);
947             ret = FALSE;
948         }
949         else
950         {
951             memset(pal_function_map, 0, PAL_API_NUMBER);
952
953             PAL_fseek(hFile, 0L, SEEK_SET);
954
955             /* Read a line of data from file: */
956             while ( PAL_fgets(line, PAL_PERF_MAX_INPUT, hFile) != NULL )
957             {
958                 if(strlen(line)==0)
959                     continue;
960                 ptr = strchr( line, '#');
961                 if( ptr )
962                     continue;
963                 sscanf_s(line, "%s %u", function_name,&index);
964
965                 if( index >= PAL_API_NUMBER)
966                 {
967                         // some code here to deal with incorrect index.
968                         // use function name to cover it.
969                 }
970                 else if(pal_function_map[index]==1)
971                 {
972                     // some code here to deal with conflict index.
973                     // use function name to cover it.
974                 }
975                 else
976                 {
977                     pal_function_map[index]=1;
978                 }
979
980             }
981
982 #ifdef PLATFORM_UNIX       
983             PAL_fclose(hFile);
984 #else
985             fclose(hFile);
986 #endif
987             ret = TRUE;
988         }
989     }
990     else
991     {
992         memset(pal_function_map, 1, PAL_API_NUMBER);
993         ret = FALSE;
994     }
995
996     if( enabledapis_path == NULL)
997     {
998         if(perf_default_path==NULL)
999         {
1000             input_file_name = NULL;
1001         }
1002         else
1003         {
1004             if( PERFIsValidFile(perf_default_path,perf_enabled_filename))
1005             {
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))
1009                 {
1010                     ret = FALSE;
1011                     input_file_name = NULL;
1012                 }
1013                 else
1014                 {
1015                     input_file_name = file_name_buf;
1016                 }
1017             }
1018             else
1019             {
1020                input_file_name=NULL;
1021             }
1022         }
1023     }
1024     else
1025     {
1026         input_file_name=enabledapis_path;
1027     }
1028
1029     if(input_file_name == NULL)
1030     {
1031         return ret; 
1032     }
1033
1034 #ifdef PLATFORM_UNIX
1035     hFile = PAL_fopen(input_file_name, "r+");
1036 #else
1037     hFile = fopen(input_file_name, "r+");
1038 #endif
1039
1040     if ( hFile != NULL )
1041     {
1042         PAL_fseek(hFile, 0L, SEEK_SET);
1043
1044         /* Read a line of data from file: */
1045         while (PAL_fgets(line, PAL_PERF_MAX_INPUT, hFile) != NULL)
1046         {
1047             if(strlen(line)==0)
1048                 continue;
1049             ptr = strchr( line, '#');
1050             if( ptr )
1051                 continue;
1052             sscanf_s(line, "%s %u", function_name,&index);
1053
1054             if( index >= PAL_API_NUMBER)
1055             {
1056                 // some code here to deal with incorrect index.
1057                 // use function name to cover it.
1058                 continue; 
1059             }
1060
1061             if (strcpy_s(API_list[index], sizeof(API_list[index]), function_name) != SAFECRT_SUCCESS)
1062             {
1063                 ret = FALSE;
1064                 break;
1065             }
1066         }
1067
1068 #ifdef PLATFORM_UNIX       
1069         PAL_fclose(hFile);
1070 #else
1071         fclose(hFile);
1072 #endif
1073     }
1074
1075     return ret;
1076
1077 }
1078
1079
1080 static
1081 BOOL
1082 PERFFlushLog(pal_perf_thread_info * local_info, BOOL output_header)
1083 {
1084     BOOL ret = FALSE;
1085     PathCharString fileName;
1086     int nWrittenBytes = 0;
1087     PERF_FILE * hFile;
1088
1089     if (summary_only)
1090         return TRUE;
1091
1092     PERFLogFileName(fileName, profile_time_log_name, "_perf_time.log");
1093
1094     hFile = PERF_FILEFN(fopen)(fileName, "a+");
1095
1096     if(hFile)
1097     {   
1098         if (output_header)
1099         {
1100             PERFPrintProgramHeaderInfo(hFile, FALSE);
1101         }
1102         if (local_info->buf_offset > 0)
1103         {
1104             nWrittenBytes = PERF_FILEFN(fwrite)(local_info->pal_write_buf, local_info->buf_offset, 1, hFile);           
1105             if (nWrittenBytes < 1)
1106             {
1107                 ERROR("fwrite() failed with errno == %d\n", errno);
1108                 return ret;
1109             }
1110             local_info->buf_offset = 0;
1111         }
1112         PERF_FILEFN(fclose)(hFile);
1113         ret = TRUE;
1114     }
1115     
1116     return ret;
1117 }
1118
1119 void
1120 PERFLogFunctionEntry(unsigned int pal_api_id, ULONGLONG *pal_perf_start_tick )
1121 {
1122     pal_perf_thread_info * local_info=NULL;
1123     pal_perf_api_info * table;
1124     char * write_buf;
1125     __int32  buf_off;
1126     short bufused = 0;
1127
1128
1129 #ifndef PLATFORM_UNIX
1130     DWORD tv;
1131     DWORD last_error;
1132     last_error = GetLastError();
1133 #else
1134     struct timeval tv;
1135 #endif
1136
1137
1138     if(!pal_perf_enabled || pal_function_map==NULL || !pal_profile_on )  // haven't initialize, just quit.
1139         return;
1140  
1141     if( pal_function_map[pal_api_id] )
1142     {
1143         local_info= (pal_perf_thread_info * )pthread_getspecific(PERF_tlsTableKey);
1144   
1145         if (local_info==NULL  )
1146         {
1147             return;
1148         }
1149         if ( !local_info->profile_enabled ) /*  prevent recursion. */
1150         {
1151             return;
1152         }
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++;
1157
1158         if(!summary_only)
1159         {            
1160             write_buf = (local_info->pal_write_buf);
1161             if(local_info->buf_offset >= PAL_PERF_BUFFER_FULL)
1162             {
1163                 PERFFlushLog(local_info, FALSE);
1164             }
1165
1166 #ifndef PLATFORM_UNIX
1167             tv = GetTickCount();
1168 #else
1169             gettimeofday(&tv, NULL);
1170 #endif
1171
1172             buf_off = local_info->buf_offset;
1173
1174 #ifndef PLATFORM_UNIX
1175             bufused = snprintf(&write_buf[buf_off], PAL_PERF_MAX_LOGLINE, "----> %d %lu entry.\n", pal_api_id, tv );
1176 #else
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 );
1178 #endif
1179             local_info->buf_offset += bufused;
1180         }
1181         if(nested_tracing)
1182             local_info->profile_enabled = TRUE; 
1183         *pal_perf_start_tick = PERFGetTicks();
1184     }
1185 #ifndef PLATFORM_UNIX
1186     SetLastError( last_error );
1187 #endif
1188     return;
1189 }
1190
1191 static
1192 void
1193 PERFUpdateApiInfo(pal_perf_api_info *api, ULONGLONG duration)
1194 {
1195     DWORD iBucket;
1196
1197     api->counter++;
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;
1204
1205     if (pal_perf_histogram_size > 0)
1206     {
1207         iBucket = (DWORD)(duration / pal_perf_histogram_step);
1208         if (iBucket >= pal_perf_histogram_size)
1209         {
1210             iBucket = pal_perf_histogram_size - 1;
1211         }
1212         api->histograms[iBucket]++;
1213     }
1214     
1215 }
1216
1217 void 
1218 PERFLogFunctionExit(unsigned int pal_api_id, ULONGLONG *pal_perf_start_tick )
1219 {
1220
1221     pal_perf_thread_info * local_info;
1222     char * buf;
1223     short bufused = 0;
1224     DWORD  off;
1225     ULONGLONG duration = 0;
1226 #ifndef PLATFORM_UNIX
1227     DWORD timev;
1228     DWORD last_error;
1229     last_error = GetLastError();
1230 #else
1231     struct timeval timev;
1232
1233 #endif
1234
1235     if(!pal_perf_enabled || (pal_function_map == NULL) || !pal_profile_on ) // haven't initiallize yet, just quit.
1236         return;
1237
1238     if (*pal_perf_start_tick != 0)
1239     {
1240         duration = PERFGetTicks() - *pal_perf_start_tick;
1241     } 
1242     else
1243     {
1244         return; // pal_perf_start_tick == 0 indicates that we exited PERFLogFunctionEntry before getting the ticks.
1245     }
1246
1247     if( pal_function_map[pal_api_id] )
1248     {
1249         local_info = (pal_perf_thread_info*)pthread_getspecific(PERF_tlsTableKey);
1250
1251         if (NULL == local_info ){
1252             return;
1253         }
1254         PERFUpdateApiInfo(&local_info->api_table[pal_api_id], duration);
1255         *pal_perf_start_tick = 0;
1256            
1257         if(summary_only)
1258         {
1259             local_info->profile_enabled = TRUE;
1260 #ifndef PLATFORM_UNIX
1261             SetLastError( last_error );
1262 #endif
1263             return;
1264         }
1265
1266 #ifndef PLATFORM_UNIX
1267         timev = GetTickCount();
1268 #else
1269         gettimeofday(&timev, NULL);
1270 #endif
1271
1272         buf = local_info->pal_write_buf;
1273         if(local_info->buf_offset >= PAL_PERF_BUFFER_FULL)
1274         {
1275             PERFFlushLog(local_info, FALSE);
1276         }
1277         off = local_info->buf_offset;
1278
1279 #ifndef PLATFORM_UNIX
1280         bufused = snprintf(&buf[off], PAL_PERF_MAX_LOGLINE, "<---- %d %lu exit. \n", pal_api_id, timev);
1281 #else
1282         bufused = snprintf(&buf[off], PAL_PERF_MAX_LOGLINE, "<---- %d %lu %06u exit. \n", pal_api_id, timev.tv_sec,  timev.tv_usec );
1283 #endif
1284         local_info->buf_offset += bufused;
1285         local_info->profile_enabled = TRUE;  
1286     }
1287 #ifndef PLATFORM_UNIX
1288     SetLastError( last_error );
1289 #endif
1290     return;
1291 }
1292
1293 void
1294 PERFNoLatencyProfileEntry(unsigned int pal_api_id )
1295 {
1296     pal_perf_thread_info * local_info=NULL;
1297     pal_perf_api_info * table;
1298 #ifndef PLATFORM_UNIX
1299     DWORD last_error;
1300     last_error = GetLastError();
1301 #endif
1302
1303     if(!pal_perf_enabled || pal_function_map==NULL || !pal_profile_on )  // haven't initialize, just quit.
1304         return;
1305     if( pal_function_map[pal_api_id] )
1306     {
1307          local_info= (pal_perf_thread_info * )pthread_getspecific(PERF_tlsTableKey);
1308          if (local_info==NULL  )
1309          {
1310 #ifndef PLATFORM_UNIX
1311             SetLastError( last_error );
1312 #endif
1313             return;
1314          }
1315          else{
1316             table = local_info->api_table;
1317             table[pal_api_id].entries++;
1318         }
1319    }
1320 #ifndef PLATFORM_UNIX
1321     SetLastError( last_error );
1322 #endif
1323    return;
1324 }
1325
1326
1327 void
1328 PERFEnableThreadProfile(BOOL isInternal)
1329 {
1330     pal_perf_thread_info * local_info;
1331 #ifndef PLATFORM_UNIX
1332     DWORD last_error;
1333     last_error = GetLastError();
1334 #endif
1335     if (!pal_perf_enabled)
1336         return;
1337     if (NULL != (local_info = (pal_perf_thread_info*)pthread_getspecific(PERF_tlsTableKey)))
1338     {
1339          if (!isInternal || nested_tracing) {
1340             local_info->profile_enabled = TRUE;
1341             local_info->start_ticks = PERFGetTicks();
1342          }
1343     }
1344 #ifndef PLATFORM_UNIX
1345     SetLastError( last_error );
1346 #endif
1347 }
1348
1349
1350 void
1351 PERFDisableThreadProfile(BOOL isInternal)
1352 {
1353     pal_perf_thread_info * local_info;
1354 #ifndef PLATFORM_UNIX
1355     DWORD last_error;
1356     last_error = GetLastError();
1357 #endif
1358     if (!pal_perf_enabled)
1359         return;
1360     if (NULL != (local_info = (pal_perf_thread_info*)pthread_getspecific(PERF_tlsTableKey)))
1361     {
1362          if (!isInternal || nested_tracing) {
1363             local_info->profile_enabled = FALSE;
1364             local_info->total_duration = PERFGetTicks() - local_info->start_ticks;
1365          }
1366     }
1367 #ifndef PLATFORM_UNIX
1368     SetLastError( last_error );
1369 #endif
1370 }
1371
1372
1373 void
1374 PERFEnableProcessProfile(  )
1375 {
1376     if (!pal_perf_enabled || wait_for_startup)
1377         return;
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();
1382 }
1383
1384
1385 void
1386 PERFDisableProcessProfile( )
1387 {
1388     if (!pal_perf_enabled)
1389         return;
1390     pal_profile_on = FALSE;
1391     // compute the total program duration in cpu clock ticks.
1392     if (program_info.start_ticks != 0)
1393     {
1394         program_info.elapsed_time += (PERFGetTicks() - program_info.start_ticks);
1395         program_info.start_ticks = 0;
1396     }
1397 }
1398
1399 BOOL
1400 PERFIsProcessProfileEnabled(  )
1401 {
1402     return pal_profile_on;
1403 }
1404
1405 static
1406 char *
1407 PERFIsValidPath( const char * path )
1408 {
1409 #ifndef PLATFORM_UNIX
1410     DWORD result;
1411 #else
1412     DIR * dir;
1413 #endif
1414
1415     if(( path==NULL) || (strlen(path)==0))
1416        return NULL; 
1417
1418 #ifndef PLATFORM_UNIX
1419     result = GetFileAttributesA( path );
1420     if ((result != INVALID_FILE_ATTRIBUTES) && (result & FILE_ATTRIBUTE_DIRECTORY))
1421     {
1422         return ((char *) path );
1423     }
1424 #else
1425     dir = opendir(path);
1426     if( dir!=NULL)
1427     {
1428         closedir(dir);
1429         return ((char *)path);
1430     }
1431 #endif
1432     return NULL;
1433 }
1434
1435 static
1436 char * 
1437 PERFIsValidFile( const char * path, const char * file)
1438 {
1439     FILE * hFile;
1440     char * temp;
1441     PathCharString tempPS;
1442
1443     if(file==NULL || strlen(file)==0) 
1444         return NULL;
1445
1446         if ( strcmp(path, "") )
1447     {
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))
1453         {
1454             tempPS.CloseBuffer(0);
1455             return NULL;
1456         }
1457
1458         tempPS.CloseBuffer(length);
1459         hFile = fopen(temp, "r");
1460     }
1461     else
1462         {
1463         hFile = fopen(file, "r");
1464         }
1465
1466     if(hFile)
1467     {
1468             fclose(hFile);
1469         return ((char *) file);
1470     }
1471         else
1472                 return NULL;
1473
1474 }
1475
1476 PALIMPORT
1477 VOID
1478 PALAPI
1479 PAL_EnableProcessProfile(VOID)
1480 {
1481     wait_for_startup = FALSE;
1482     pal_profile_on = TRUE;
1483     PERFEnableProcessProfile();
1484 }
1485
1486 PALIMPORT
1487 VOID
1488 PALAPI
1489 PAL_DisableProcessProfile(VOID)
1490 {
1491     pal_profile_on = FALSE;
1492     PERFDisableProcessProfile();
1493 }
1494
1495 PALIMPORT
1496 BOOL
1497 PALAPI
1498 PAL_IsProcessProfileEnabled(VOID)
1499 {
1500     return PERFIsProcessProfileEnabled();
1501 }
1502
1503 PALIMPORT
1504 INT64
1505 PALAPI
1506 PAL_GetCpuTickCount(VOID)
1507 {
1508     return PERFGetTicks();
1509 }
1510
1511 #ifndef PLATFORM_UNIX
1512 #undef snprintf 
1513 #undef MiscGetenv
1514 #undef pthread_key_t
1515 #undef pthread_getspecific
1516 #endif /* ifndef PLATFORM_UNIX definitions */
1517
1518 #endif /* PAL_PERF */
1519
1520
1521
1522