Fix trigger for tier 1 call counting delay (#17477)
[platform/upstream/coreclr.git] / src / binder / cdebuglog.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 // CDebugLog.cpp
7 //
8
9
10 //
11 // Implements the fusion-derived CDebugLog class
12 //
13 // ============================================================
14
15 #ifdef FEATURE_VERSIONING_LOG
16
17 #include "cdebuglog.hpp"
18 #include "applicationcontext.hpp"
19 #include "assemblyname.hpp"
20 #include "variables.hpp"
21 #include "utils.hpp"
22
23 #include "shlwapi.h"
24 #include "strsafe.h"
25
26 #include "../dlls/mscorrc/fusres.h"
27
28 #define MAX_DBG_STR_LEN 1024
29 #define MAX_DATE_LEN    128
30
31 #define DEBUG_LOG_HTML_START         L"<html><pre>\r\n"
32 #define DEBUG_LOG_HTML_META_LANGUAGE L"<meta http-equiv=\"Content-Type\" content=\"charset=unicode-1-1-utf-8\">"
33 #define DEBUG_LOG_MARK_OF_THE_WEB    L"<!-- saved from url=(0015)assemblybinder: -->"
34 #define DEBUG_LOG_HTML_END           L"\r\n</pre></html>"
35 #define DEBUG_LOG_NEW_LINE           L"\r\n"
36
37 namespace BINDER_SPACE
38 {
39     namespace
40     {
41         inline LPCWSTR LogCategoryToString(DWORD dwLogCategory)
42         {
43             switch (dwLogCategory)
44             {
45             case FUSION_BIND_LOG_CATEGORY_DEFAULT:
46                 return L"default";
47             case FUSION_BIND_LOG_CATEGORY_NGEN:
48                 return L"Native";
49             default:
50                 return L"Unknown";
51             }
52         }
53
54         HRESULT CreateFilePathHierarchy(LPCOLESTR pszName)
55         {
56             HRESULT hr=S_OK;
57             LPTSTR  pszFileName;
58             PathString szPathString;
59             DWORD   dw = 0;
60
61             size_t pszNameLen = wcslen(pszName);
62             WCHAR * szPath = szPathString.OpenUnicodeBuffer(static_cast<COUNT_T>(pszNameLen));
63             size_t cbSzPath = (sizeof(WCHAR)) * (pszNameLen + 1); // SString allocates extra byte for null
64             IF_FAIL_GO(StringCbCopy(szPath, cbSzPath, pszName));
65             szPathString.CloseBuffer(static_cast<COUNT_T>(pszNameLen));
66             
67             pszFileName = PathFindFileName(szPath);
68
69             if (pszFileName <= szPath)
70             {
71                 IF_FAIL_GO(E_INVALIDARG);
72             }
73
74             *(pszFileName-1) = 0;
75
76             dw = WszGetFileAttributes(szPath);
77             if (dw != INVALID_FILE_ATTRIBUTES)
78             {
79                 return S_OK;
80             }
81         
82             hr = HRESULT_FROM_GetLastError();
83
84             switch (hr)
85             {
86             case __HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND):
87             {
88                 hr =  CreateFilePathHierarchy(szPath);
89                 if (hr != S_OK)
90                     return hr;
91             }
92
93             case __HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND):
94             {
95                 if (WszCreateDirectory(szPath, NULL))
96                     return S_OK;
97                 else
98                 {
99                     hr = HRESULT_FROM_WIN32(GetLastError());
100                     if(hr == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS))
101                         hr = S_OK;
102                     else
103                         return hr;
104                 }
105             }
106
107             default:
108                 break;
109             }
110
111         Exit:
112             return hr;
113         }
114
115         HRESULT WriteLog(HANDLE  hLogFile,
116                          LPCWSTR pwzInfo)
117         {
118             HRESULT hr = S_OK;
119             DWORD dwLen = 0;
120             DWORD dwWritten = 0;
121             CHAR szBuf[MAX_DBG_STR_LEN];
122
123             dwLen = WszWideCharToMultiByte(CP_UTF8,
124                                            0,
125                                            pwzInfo,
126                                            -1,
127                                            szBuf,
128                                            MAX_DBG_STR_LEN,
129                                            NULL,
130                                            NULL);
131
132             if (!dwLen)
133             {
134                 IF_FAIL_GO(HRESULT_FROM_GetLastError());
135             }
136
137             // dwLen includes NULL terminator. We don't want to write this out.
138             if (dwLen > 1) {
139                 dwLen--;
140
141                 if (!WriteFile(hLogFile, szBuf, dwLen, &dwWritten, NULL)) {
142                     IF_FAIL_GO(HRESULT_FROM_GetLastError());
143                 }
144             }
145
146         Exit:
147             return hr;
148         }
149
150         HRESULT GetBindTimeInfo(PathString &info)
151         {
152             HRESULT hr = S_OK;
153             SYSTEMTIME systime;
154
155             {
156                 WCHAR wzDateBuffer[MAX_DATE_LEN];
157                 WCHAR wzTimeBuffer[MAX_DATE_LEN];
158
159                 GetLocalTime(&systime);
160
161                 if (!WszGetDateFormat(LOCALE_USER_DEFAULT,
162                                       0,
163                                       &systime,
164                                       NULL,
165                                       wzDateBuffer,
166                                       MAX_DATE_LEN))
167                 {
168                     return HRESULT_FROM_GetLastError();
169                 }
170     
171                 if (!WszGetTimeFormat(LOCALE_USER_DEFAULT,
172                                       0,
173                                       &systime,
174                                       NULL,
175                                       wzTimeBuffer,
176                                       MAX_DATE_LEN))
177                 {
178                    return HRESULT_FROM_GetLastError();
179                 }
180
181                 info.Printf(L"(%s @ %s)", wzDateBuffer, wzTimeBuffer);
182             }
183             return hr;
184         }
185
186         HRESULT GetHrResultInfo(PathString &info, HRESULT hrResult)
187         {
188             HRESULT hr = S_OK;
189
190             // TODO: Get the result information in here.
191             info.Printf(L"%p.", hrResult);
192
193             return hr;
194         }
195
196         inline BOOL IsInvalidCharacter(WCHAR wcChar)
197         {
198             switch (wcChar)
199             {
200             case L':':
201             case L'/':
202             case L'\\':
203             case L'*':
204             case L'<':
205             case L'>':
206             case L'?':
207             case L'|':
208             case L'"':
209                 return TRUE;
210             default:
211                 return FALSE;
212             }
213         }
214
215         inline void ReplaceInvalidFileCharacters(SString &assemblyName)
216         {
217             SString::Iterator pos = assemblyName.Begin();
218             SString::Iterator end = assemblyName.End();
219
220             while (pos < end)
221             {
222                 if (IsInvalidCharacter(pos[0]))
223                 {
224                     assemblyName.Replace(pos, L'_');
225                 }
226
227                 pos++;
228             }
229         }
230     };
231
232     CDebugLog::CDebugLog()
233     {
234         m_cRef = 1;
235     }
236
237     CDebugLog::~CDebugLog()
238     {
239         // Nothing to do here
240     }
241
242     /* static */
243     HRESULT CDebugLog::Create(ApplicationContext  *pApplicationContext,
244                               AssemblyName        *pAssemblyName,
245                               SString             &sCodeBase,
246                               CDebugLog          **ppCDebugLog)
247     {
248         HRESULT hr = S_OK;
249         BINDER_LOG_ENTER(L"CDebugLog::Create");
250         ReleaseHolder<CDebugLog> pDebugLog;
251
252         // Validate input arguments
253         IF_FALSE_GO(pApplicationContext != NULL);
254         IF_FALSE_GO(ppCDebugLog != NULL);
255
256         SAFE_NEW(pDebugLog, CDebugLog);
257         IF_FAIL_GO(pDebugLog->Init(pApplicationContext, pAssemblyName, sCodeBase));
258
259         *ppCDebugLog = pDebugLog.Extract();
260
261     Exit:
262         BINDER_LOG_LEAVE_HR(L"CDebugLog::Create", hr);
263         return hr;
264     }
265
266     ULONG CDebugLog::AddRef()
267     {
268         return InterlockedIncrement(&m_cRef);
269     }
270
271     ULONG CDebugLog::Release()
272     {
273         ULONG ulRef;
274
275         ulRef = InterlockedDecrement(&m_cRef);
276     
277         if (ulRef == 0)
278         {
279             delete this;
280         }
281
282         return ulRef;
283     }
284
285     HRESULT CDebugLog::SetResultCode(DWORD   dwLogCategory,
286                                      HRESULT hrResult)
287     {
288         HRESULT hr = S_OK;
289         BINDER_LOG_ENTER(L"CDebugLog::SetResultCode");
290
291         IF_FALSE_GO(dwLogCategory < FUSION_BIND_LOG_CATEGORY_MAX);
292
293         m_HrResult[dwLogCategory] = hrResult;
294
295     Exit:
296         BINDER_LOG_LEAVE_HR(L"CDebugLog::SetResultCode", hr);
297         return hr;
298     }
299
300     HRESULT CDebugLog::LogMessage(DWORD,
301                                   DWORD    dwLogCategory, 
302                                   SString &sDebugString)
303     {
304         HRESULT hr = S_OK;
305         BINDER_LOG_ENTER(L"CDebugLog::LogMessage");
306
307         IF_FALSE_GO(dwLogCategory < FUSION_BIND_LOG_CATEGORY_MAX);
308
309         m_content[dwLogCategory].AddTail(const_cast<const SString &>(sDebugString));
310
311     Exit:
312         BINDER_LOG_LEAVE_HR(L"CDebugLog::LogMessage", hr);
313         return hr;
314     }
315
316     HRESULT CDebugLog::Flush(DWORD,
317                              DWORD dwLogCategory)
318     {
319         HRESULT hr = S_OK;
320         BINDER_LOG_ENTER(L"CDebugLog::Flush");
321         SmallStackSString sCategory(LogCategoryToString(dwLogCategory));
322         PathString logFilePath;
323         ListNode<SString> *pListNode = NULL;
324
325         IF_FALSE_GO(dwLogCategory < FUSION_BIND_LOG_CATEGORY_MAX);
326
327         CombinePath(g_BinderVariables->logPath, sCategory, logFilePath);
328         CombinePath(logFilePath, m_applicationName, logFilePath);
329         CombinePath(logFilePath, m_logFileName, logFilePath);
330
331         BINDER_LOG_STRING(L"logFilePath", logFilePath);
332
333         IF_FAIL_GO(CreateFilePathHierarchy(logFilePath.GetUnicode()));
334
335         m_hLogFile = WszCreateFile(logFilePath.GetUnicode(),
336                                    GENERIC_READ | GENERIC_WRITE,
337                                    0,
338                                    NULL,
339                                    CREATE_ALWAYS,
340                                    FILE_ATTRIBUTE_NORMAL,
341                                    NULL);
342
343         if (m_hLogFile == INVALID_HANDLE_VALUE)
344         {
345             // Silently ignore unability to log.
346             BINDER_LOG(L"Unable to open binding log");
347             GO_WITH_HRESULT(S_OK);
348         }
349
350         LogHeader(dwLogCategory);
351
352         pListNode = static_cast<ListNode<SString> *>(m_content[dwLogCategory].GetHeadPosition());
353         while (pListNode != NULL)
354         {
355             SString item = pListNode->GetItem();
356
357             IF_FAIL_GO(WriteLog(m_hLogFile, item.GetUnicode()));
358             IF_FAIL_GO(WriteLog(m_hLogFile, DEBUG_LOG_NEW_LINE));
359
360             pListNode = pListNode->GetNext();
361         }
362
363         LogFooter(dwLogCategory);
364
365         // Ignore failure
366         CloseHandle(m_hLogFile.Extract());
367
368     Exit:
369         BINDER_LOG_LEAVE_HR(L"CDebugLog::Flush", hr);
370         return hr;
371     }
372
373     HRESULT CDebugLog::Init(ApplicationContext *pApplicationContext,
374                             AssemblyName       *pAssemblyName,
375                             SString            &sCodeBase)
376     {
377         HRESULT hr = S_OK;
378         BINDER_LOG_ENTER(L"CDebugLog::Init");
379
380         m_applicationName.Set(pApplicationContext->GetApplicationName());
381         ReplaceInvalidFileCharacters(m_applicationName);
382
383         if (m_applicationName.IsEmpty())
384         {
385             BINDER_LOG(L"empty application name");
386             m_applicationName.Set(L"unknown");
387         }
388
389         if (pAssemblyName == NULL)
390         {
391             m_logFileName.Set(L"WhereRefBind!Host=(LocalMachine)!FileName=(");
392
393             LPCWSTR pwzFileName = PathFindFileNameW(sCodeBase.GetUnicode());
394             if (pwzFileName != NULL)
395             {
396                 m_logFileName.Append(pwzFileName);
397             }
398             m_logFileName.Append(L").HTM");
399         }
400         else
401         {
402             PathString assemblyDisplayName;
403
404             pAssemblyName->GetDisplayName(assemblyDisplayName,
405                                           AssemblyName::INCLUDE_VERSION |
406                                           AssemblyName::INCLUDE_ARCHITECTURE |
407                                           AssemblyName::INCLUDE_RETARGETABLE);
408
409             ReplaceInvalidFileCharacters(assemblyDisplayName);
410
411             m_logFileName.Set(assemblyDisplayName);
412             m_logFileName.Append(L".HTM");
413         }
414
415         BINDER_LOG_LEAVE_HR(L"CDebugLog::Init", hr);
416         return hr;
417     }
418
419     HRESULT CDebugLog::LogHeader(DWORD dwLogCategory)
420     {
421         HRESULT hr = S_OK;
422         BINDER_LOG_ENTER(L"CDebugLog::LogHeader");
423         PathString info;
424         PathString temp;
425         PathString format;
426
427         IF_FAIL_GO(WriteLog(m_hLogFile, DEBUG_LOG_HTML_META_LANGUAGE));
428         IF_FAIL_GO(WriteLog(m_hLogFile, DEBUG_LOG_MARK_OF_THE_WEB));
429         IF_FAIL_GO(WriteLog(m_hLogFile, DEBUG_LOG_HTML_START));
430
431         IF_FAIL_GO(GetBindTimeInfo(temp));
432         IF_FAIL_GO(format.LoadResourceAndReturnHR(CCompRC::Debugging, ID_FUSLOG_BINDING_HEADER_BEGIN));
433         info.Printf(format.GetUnicode(), temp.GetUnicode());
434         IF_FAIL_GO(WriteLog(m_hLogFile, info.GetUnicode()));
435         IF_FAIL_GO(WriteLog(m_hLogFile, DEBUG_LOG_NEW_LINE DEBUG_LOG_NEW_LINE));
436
437         if (SUCCEEDED(m_HrResult[dwLogCategory]))
438         {
439             IF_FAIL_GO(temp.
440                        LoadResourceAndReturnHR(CCompRC::Debugging, ID_FUSLOG_BINDING_HEADER_BIND_RESULT_SUCCESS));
441             IF_FAIL_GO(WriteLog(m_hLogFile, temp.GetUnicode()));
442         }
443         else
444         {
445             IF_FAIL_GO(temp.
446                        LoadResourceAndReturnHR(CCompRC::Debugging, ID_FUSLOG_BINDING_HEADER_BIND_RESULT_ERROR));
447             IF_FAIL_GO(WriteLog(m_hLogFile, temp.GetUnicode()));
448         }
449         IF_FAIL_GO(WriteLog(m_hLogFile, DEBUG_LOG_NEW_LINE));
450
451         GetHrResultInfo(temp, m_HrResult[dwLogCategory]);
452         
453         IF_FAIL_GO(format.LoadResourceAndReturnHR(CCompRC::Debugging, ID_FUSLOG_BINDING_HEADER_BIND_RESULT));
454         info.Printf(format.GetUnicode(), temp.GetUnicode());
455         IF_FAIL_GO(WriteLog(m_hLogFile, info.GetUnicode()));
456         IF_FAIL_GO(WriteLog(m_hLogFile, DEBUG_LOG_NEW_LINE DEBUG_LOG_NEW_LINE));
457
458         // TODO: Assembly Manager info + Executable info.
459
460         IF_FAIL_GO(info.LoadResourceAndReturnHR(CCompRC::Debugging, ID_FUSLOG_BINDING_HEADER_END));
461         IF_FAIL_GO(WriteLog(m_hLogFile, info.GetUnicode()));
462         IF_FAIL_GO(WriteLog(m_hLogFile, DEBUG_LOG_NEW_LINE DEBUG_LOG_NEW_LINE));
463
464     Exit:
465         BINDER_LOG_LEAVE_HR(L"CDebugLog::LogHeader", hr);
466         return hr;
467     }
468
469     HRESULT CDebugLog::LogFooter(DWORD)
470     {
471         HRESULT hr = S_OK;
472         BINDER_LOG_ENTER(L"CDebugLog::LogFooter");
473
474         IF_FAIL_GO(WriteLog(m_hLogFile, DEBUG_LOG_HTML_END));
475
476     Exit:
477         BINDER_LOG_LEAVE_HR(L"CDebugLog::LogFooter", hr);
478         return hr;
479     }
480 };
481
482 #endif // FEATURE_VERSIONING_LOG