1//
2// Copyright (c) Microsoft. All rights reserved.
3// Licensed under the MIT license. See LICENSE file in the project root for full license information.
4//
5
6//-----------------------------------------------------------------------------
7// MethodStatsEmitter.cpp - Emits useful method stats for compiled methods for analysis
8//-----------------------------------------------------------------------------
9
10#include "standardpch.h"
11#include "methodstatsemitter.h"
12#include "logging.h"
13
14MethodStatsEmitter::MethodStatsEmitter(char* nameOfInput)
15{
16 char filename[MAX_PATH + 1];
17 sprintf_s(filename, MAX_PATH + 1, "%s.stats", nameOfInput);
18
19 hStatsFile =
20 CreateFileA(filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
21 if (hStatsFile == INVALID_HANDLE_VALUE)
22 {
23 LogError("Failed to open output file '%s'. GetLastError()=%u", filename, GetLastError());
24 }
25}
26
27MethodStatsEmitter::~MethodStatsEmitter()
28{
29 if (hStatsFile != INVALID_HANDLE_VALUE)
30 {
31 if (CloseHandle(hStatsFile) == 0)
32 {
33 LogError("CloseHandle failed. GetLastError()=%u", GetLastError());
34 }
35 }
36}
37
38void MethodStatsEmitter::Emit(int methodNumber, MethodContext* mc, ULONGLONG firstTime, ULONGLONG secondTime)
39{
40 if (hStatsFile != INVALID_HANDLE_VALUE)
41 {
42 // Print the CSV header row
43 char rowData[2048];
44 DWORD charCount = 0;
45 DWORD bytesWritten = 0;
46
47 if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'h') != NULL || strchr(statsTypes, 'H') != NULL)
48 {
49 // Obtain the method Hash
50 char md5Hash[MD5_HASH_BUFFER_SIZE];
51 if (mc->dumpMethodMD5HashToBuffer(md5Hash, MD5_HASH_BUFFER_SIZE) != MD5_HASH_BUFFER_SIZE)
52 md5Hash[0] = 0;
53
54 charCount += sprintf_s(rowData + charCount, _countof(rowData) - charCount, "%s,", md5Hash);
55 }
56 if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'n') != NULL || strchr(statsTypes, 'N') != NULL)
57 {
58 charCount += sprintf_s(rowData + charCount, _countof(rowData) - charCount, "%d,", methodNumber);
59 }
60 if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'i') != NULL || strchr(statsTypes, 'I') != NULL)
61 {
62 // Obtain the IL code size for this method
63 CORINFO_METHOD_INFO info;
64 unsigned flags = 0;
65 mc->repCompileMethod(&info, &flags);
66
67 charCount += sprintf_s(rowData + charCount, _countof(rowData) - charCount, "%d,", info.ILCodeSize);
68 }
69 if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'a') != NULL || strchr(statsTypes, 'A') != NULL)
70 {
71 // Obtain the compiled method ASM size
72 BYTE* temp;
73 DWORD codeSize;
74 CorJitResult result;
75 if (mc->cr->CompileMethod != nullptr)
76 mc->cr->repCompileMethod(&temp, &codeSize, &result);
77 else
78 codeSize = 0; // this is likely a thin mc
79
80 charCount += sprintf_s(rowData + charCount, _countof(rowData) - charCount, "%d,", codeSize);
81 }
82 if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 't') != NULL || strchr(statsTypes, 'T') != NULL)
83 {
84 charCount +=
85 sprintf_s(rowData + charCount, _countof(rowData) - charCount, "%llu,%llu,", firstTime, secondTime);
86 }
87
88 // get rid of the final ',' and replace it with a '\n'
89 rowData[charCount - 1] = '\n';
90
91 if (!WriteFile(hStatsFile, rowData, charCount, &bytesWritten, nullptr) || bytesWritten != charCount)
92 {
93 LogError("Failed to write row header '%s'. GetLastError()=%u", rowData, GetLastError());
94 }
95 }
96}
97
98void MethodStatsEmitter::SetStatsTypes(char* types)
99{
100 statsTypes = types;
101
102 if (hStatsFile != INVALID_HANDLE_VALUE)
103 {
104 // Print the CSV header row
105 char rowHeader[1024];
106 DWORD charCount = 0;
107 DWORD bytesWritten = 0;
108
109 if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'h') != NULL || strchr(statsTypes, 'H') != NULL)
110 charCount += sprintf_s(rowHeader + charCount, _countof(rowHeader) - charCount, "HASH,");
111 if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'n') != NULL || strchr(statsTypes, 'N') != NULL)
112 charCount += sprintf_s(rowHeader + charCount, _countof(rowHeader) - charCount, "METHOD_NUMBER,");
113 if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'i') != NULL || strchr(statsTypes, 'I') != NULL)
114 charCount += sprintf_s(rowHeader + charCount, _countof(rowHeader) - charCount, "IL_CODE_SIZE,");
115 if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'a') != NULL || strchr(statsTypes, 'A') != NULL)
116 charCount += sprintf_s(rowHeader + charCount, _countof(rowHeader) - charCount, "ASM_CODE_SIZE,");
117 if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 't') != NULL || strchr(statsTypes, 'T') != NULL)
118 charCount += sprintf_s(rowHeader + charCount, _countof(rowHeader) - charCount, "Time1,Time2,");
119
120 // get rid of the final ',' and replace it with a '\n'
121 rowHeader[charCount - 1] = '\n';
122
123 if (!WriteFile(hStatsFile, rowHeader, charCount, &bytesWritten, nullptr) || bytesWritten != charCount)
124 {
125 LogError("Failed to write row header '%s'. GetLastError()=%u", rowHeader, GetLastError());
126 }
127 }
128}
129