{"id":188,"date":"2007-01-25T11:03:14","date_gmt":"2007-01-25T09:03:14","guid":{"rendered":"https:\/\/alax.info\/blog\/188"},"modified":"2007-01-25T11:03:14","modified_gmt":"2007-01-25T09:03:14","slug":"how-to-print-function-names-to-debug-output-with-atls-atltrace-macro","status":"publish","type":"post","link":"https:\/\/alax.info\/blog\/188","title":{"rendered":"How to print function names to debug output with ATL&#8217;s ATLTRACE macro"},"content":{"rendered":"<p>Starting Visual Studio .NET version 2003 (2002?) ATL offered new tracing macros ATLTRACE\/<a href=\"http:\/\/msdn2.microsoft.com\/en-us\/dhxsse89(VS.80).aspx\">ATLTRACE2<\/a> which are quite convenient and flexible to generate debug output and, what is important, come with ATL debug API which allows to adjust output settings on runtime using <a href=\"http:\/\/msdn2.microsoft.com\/en-us\/ms241448(VS.80).aspx\">ATL\/MFC Trace Tool<\/a>.<\/p>\n<p>The problem however appeared to be that ATL debug API does not seem to support printing function names, although trace tools shows checbox &#8220;Category &#038; Function Names&#8221;. I am solving this shortage.<\/p>\n<p><!--more--><\/p>\n<p>The way to solve the problem is in updating ATL source code, <em>atltrace.h<\/em> file. One can either modify the file in ATL include directory, or, the way I prefer, is to make a copy of the file, update it and include before other ATL includes to include instead of original header file. There will be no updated file because it is based on ATL code which, I believe, requires special permission to distribute. Changes are mentioned below though.<br \/>\nFirst of all, at the very top, a replacement header file requires definition of <strong>__ATLTRACE_H__<\/strong> to prevent from inclusion of original <em>atltrace.h<\/em>:<\/p>\n<blockquote><p><em>#define __ATLTRACE_H__ \/\/ This is a replacement of ATL&#8217;s atltrace.h<\/em><\/p><\/blockquote>\n<p>Then we definitely have to update ATLRACE and ATLTRACE2 macro definition to involve function name <a href=\"http:\/\/msdn2.microsoft.com\/en-us\/library\/ms253913(vs.80).aspx\"><strong>__FUNCTION__<\/strong><\/a>:<\/p>\n<blockquote><p><em>#define ATLTRACE ATL::CTraceFileLineFunctionInfo(__FILE__, __LINE__, __FUNCTION__)<br \/>\n#define ATLTRACE2 ATLTRACE<\/em><\/p><\/blockquote>\n<p>CTraceFileLineFunctionInfo is a new class based on original CTraceFileAndLineInfo to also involve __FUNCTION__ parameter and pass it through the calls to ATL::CTrace::s_trace.TraceV, which in its turn also needs an extra parameter to be added.<\/p>\n<p>TraceV uses ATL debug API <em>AtlTraceVA<\/em>\/<em>AtlTraceVU<\/em> call to actually print debug output. It does not show us a parameter for function name, so we have  to inject it before variable list of arguments. Updated TraceV is shown below (WCHAR version is updated correspondingly):<\/p>\n<blockquote><p><em>inline VOID __cdecl CTrace::TraceV(LPCSTR pszFileName, INT nLine, LPCSTR pszFunctionName, DWORD_PTR nCategory, UINT nLevel, LPCSTR pszFormat, va_list Arguments) const<br \/>\n{<br \/>\n#if defined(_DEBUG)<br \/>\nCHAR pszFormatEx[4096] = { 0 };<br \/>\nstrcpy_s(pszFormatEx, _countof(pszFormatEx), &#8220;%hs: &#8220;);<br \/>\nstrcat_s(pszFormatEx, _countof(pszFormatEx), pszFormat);<br \/>\nstruct { LPCSTR pszFunctionName; BYTE pnBuffer[1024 &#8211; sizeof (LPCSTR)]; } ArgumentsEx;<br \/>\nArgumentsEx.pszFunctionName = pszFunctionName;<br \/>\nif(Arguments)<br \/>\n{<br \/>\nSIZE_T nArgumentsSize = ATL::GetTraceArgumentsSize(pszFormat, Arguments);<br \/>\nATLASSERT(!IsBadReadPtr(Arguments, nArgumentsSize));<br \/>\nATLASSERT(nArgumentsSize <= sizeof ArgumentsEx.pnBuffer);\nCopyMemory(ArgumentsEx.pnBuffer, Arguments, nArgumentsSize);\n}\nAtlTraceVA(m_nModule, pszFileName ? strrchr(pszFileName, '\\\\') + 1 : pszFileName, nLine, nCategory, nLevel, pszFormatEx, (va_list) &#038;ArgumentsEx);\n#else\npszFunctionName;\nAtlTraceVA(m_nModule, pszFileName, nLine, nCategory, nLevel, pszFormat, Arguments);\n#endif \/\/ defined(_DEBUG)\n}<\/em><\/p><\/blockquote>\n<p>To print function names in front of debug output we are inserting <em>&#8220;%hs: &#8220;<\/em> format before the provided <em>pszFormat<\/em> and we also need to insert an <em>LPCSTR<\/em> pointer into the beginning of variable argument list <em>Arguments<\/em>. We need to find list size, for simplicity (unexact) it can be obtained using the following function <em>GetTraceArgumentsSize<\/em>:<\/p>\n<blockquote><p><em>inline SIZE_T GetTraceArgumentsSize(LPCSTR pszFormat, va_list Arguments)<br \/>\n{<br \/>\nSIZE_T nSize = 8 * sizeof (BYTE*);<br \/>\nif(pszFormat)<br \/>\nfor(LPCSTR pszPointer = pszFormat; ; )<br \/>\n{<br \/>\nLPCSTR pszOccurrence = strchr(pszPointer, &#8216;%&#8217;);<br \/>\nif(!pszOccurrence)<br \/>\nbreak;<br \/>\nnSize += sizeof (BYTE*);<br \/>\npszPointer = pszOccurrence + 1;<br \/>\n}<br \/>\nreturn nSize;<br \/>\n}<\/em><\/p><\/blockquote>\n<p>In order to not flood output with file paths, <em>strrchr<\/em> is used to strip source file directory and print file name only. It does not appear to be a problem with Visual Studio .NET IDE to remain lines clickable and navigating to source code position.<\/p>\n<p>Update complete? Yes:<\/p>\n<blockquote><p><em>application.h(424) : atlTraceGeneral: <strong>CApplication::Start<\/strong>: &#8230;<br \/>\naiatlcom.h(651) : atlTraceGeneral: <strong>CThreadT<class CListenSocket>::Initialize<\/strong>: Thread 4828 (0x12dc) has been started from thread 2984<br \/>\naiatlcom.h(588) : atlTraceGeneral: <strong>CThreadT<class CListenSocket>::ThreadProc<\/strong>: Thread 4828 (0x12dc) has been started<\/em><\/p><\/blockquote>\n<p>Function names are in debug output now. Very convenient!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Starting Visual Studio .NET version 2003 (2002?) ATL offered new tracing macros ATLTRACE\/ATLTRACE2 which are quite convenient and flexible to generate debug output and, what is important, come with ATL debug API which allows to adjust output settings on runtime using ATL\/MFC Trace Tool. The problem however appeared to be that ATL debug API does&hellip; <\/p>\n<p><a class=\"moretag\" href=\"https:\/\/alax.info\/blog\/188\">Read the full article<\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11],"tags":[],"class_list":["post-188","post","type-post","status-publish","format-standard","hentry","category-atl"],"_links":{"self":[{"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/posts\/188","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/comments?post=188"}],"version-history":[{"count":0,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/posts\/188\/revisions"}],"wp:attachment":[{"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/media?parent=188"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/categories?post=188"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/tags?post=188"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}