Всем привет!
Вижу по форуму катается перекати поле, поэтому, чтобы вы не скучали в отсутствии задачек от школоты и студентов, вот вам мой говнокод :
CStackTracer
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
class CStackTracer
{
public:
CStackTracer(void)
{
m_vLog.reserve(100UL);
m_n64StartAt = ::GetTickCount64();
m_bErrorDetected = false;
}
~CStackTracer(void)
{
Flush(TRUE);
}
private:
CStackTracer(const CStackTracer& other) = delete;
CStackTracer& operator=(const CStackTracer& rhs) = delete;
public:
CStackTracer& operator () (LPCTSTR lpFunc, int nLine)
{
StepIn(lpFunc, nLine, ::GetTickCount64());
return *this;
}
protected:
std::vector<LPTSTR> m_vLog;
__int64 m_n64StartAt;
bool m_bErrorDetected;
public:
void Flush(BOOL bDisplayStackTrace = FALSE)
{
while (m_vLog.size())
{
auto rec = m_vLog.back();
if (bDisplayStackTrace)
OutputDebugString(rec);
::free(rec);
m_vLog.pop_back();
}
m_bErrorDetected = false;
}
void StepIn(LPCTSTR lpFunc, int nLine, __int64 n64EndAt)
{
m_vLog.push_back(FormatString(_T("\t%s @%d, time: %I64u\n"), lpFunc, nLine, n64EndAt - m_n64StartAt));
m_n64StartAt = n64EndAt;
}
void StepOut(void)
{
if (m_bErrorDetected == false && m_vLog.size())
{
auto last_one = m_vLog.back();
m_vLog.pop_back();
::free(last_one);
}
if (m_vLog.empty())
m_bErrorDetected = false;
}
BOOL ErrorImpl(DWORD dwErrCode, LPCTSTR lpFunc, int nLine)
{
LPTSTR lpErrDescr = GetErrDescription(dwErrCode);
m_vLog.push_back(FormatString(L"\t%s @%d, time: %I64u [err = 0x%08X (%s)]\n",
lpFunc, nLine, ::GetTickCount64() - m_n64StartAt,
dwErrCode, lpErrDescr));
::free(lpErrDescr);
m_bErrorDetected = true;
return dwErrCode == ERROR_SUCCESS;
}
BOOL Error(DWORD dwErrCode, LPCTSTR lpFunc, int nLine)
{
ErrorImpl(dwErrCode, lpFunc, nLine);
return FALSE;
}
template<typename ... Args>
BOOL Error(DWORD dwErrCode, LPCTSTR lpFunc, int nLine, Args ... vArgs)
{
ErrorImpl(dwErrCode, lpFunc, nLine, vArgs...);
return FALSE;
}
protected:
template<typename ...Args>
static LPTSTR FormatString(LPCTSTR lpFmt, Args ... vArgs)
{
size_t cchDest = 256UL;
size_t cchMem = (cchDest + 1UL) * sizeof(TCHAR);
TCHAR* pszDest = static_cast<TCHAR*>(::malloc(cchMem));
if (pszDest == nullptr)
{
::SetLastError(ERROR_OUTOFMEMORY);
return nullptr;
}
::ZeroMemory(pszDest, cchMem);
HRESULT hRet = ::StringCchPrintf(pszDest, cchDest, lpFmt, vArgs...);
if (SUCCEEDED(hRet))
return pszDest;
::free(pszDest);
::SetLastError(hRet);
return nullptr;
}
static LPTSTR GetErrDescription(DWORD dwErrCode, HMODULE hErrDescSource = NULL)
{
DWORD dwMem = 64UL * 1024UL;
DWORD dwMaxMsgLen = (dwMem / sizeof(TCHAR)) - 1UL;
auto lpErrDesc = static_cast<TCHAR*>(::malloc(dwMem));
if (lpErrDesc == NULL)
{
::SetLastError(ERROR_OUTOFMEMORY);
return NULL;
}
::ZeroMemory(lpErrDesc, dwMem);
DWORD dwFlags = FORMAT_MESSAGE_IGNORE_INSERTS;
if (hErrDescSource)
dwFlags |= FORMAT_MESSAGE_FROM_HMODULE;
else
dwFlags |= FORMAT_MESSAGE_FROM_SYSTEM;
if (::FormatMessage(dwFlags, hErrDescSource, dwErrCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), lpErrDesc, dwMaxMsgLen, NULL))
{
size_t szLen = ::_tcslen(lpErrDesc);
LPTSTR lpEnd = lpErrDesc + szLen;
while (--lpEnd > lpErrDesc && (*lpEnd == _T('\r') || *lpEnd == _T('\n')))
{
*lpEnd = _T('\0');
}
return lpErrDesc;
}
::free(lpErrDesc);
return NULL;
}
}; // class CStackTracer
CLog
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
class CLog
{
public:
CLog(CStackTracer& st, LPCTSTR lpFunc, int nLine) : m_st(st(lpFunc, nLine))
{
}
~CLog(void)
{
m_st.StepOut();
}
private:
CLog(const CLog& other) = delete;
CLog& operator=(const CLog& rhs) = delete;
public:
operator CStackTracer&() const { return m_st; }
protected:
CStackTracer& m_st;
}; // class CLog
CLogTest
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
class CLogTest
{
public:
CLogTest(void) = default;
~CLogTest(void) = default;
private:
CLogTest(const CLogTest& other) = delete;
CLogTest& operator=(const CLogTest& rhs) = delete;
public:
BOOL foo_1(CStackTracer& st, bool bEndWithError) const
{
::Sleep(100UL);
return foo_2(LOGTRACE(st), bEndWithError);
}
BOOL foo_2(CStackTracer& st, bool bEndWithError) const
{
::Sleep(50UL);
if (foo_3(LOGTRACE(st), bEndWithError) == FALSE)
return LOGERROR(ERROR_ACCESS_DENIED);
return TRUE;
}
BOOL foo_3(CStackTracer& st, bool bEndWithError) const
{
::Sleep(200UL);
if (bEndWithError)
return LOGERROR(ERROR_BAD_USERNAME);
return TRUE;
}
}; // class CLogTest
Как не трудно догадаться это трейсер вызовов функций с их логированием.
Разумеется в нем нет, по большей части, проверок и контроля ошибок, все сделано ради максимальной простоты и наглядности, т.к. вопрос у меня заключается не в этом.
Код вполне рабочий и выводит на экран примерно следующее:
outputCLogTest::foo_2 @28, time: 204 [err = 0x00000005 (Access is denied.)]
CLogTest::foo_3 @37, time: 204 [err = 0x0000089A (The specified username is invalid.)]
CLogTest::foo_2 @27, time: 46
CLogTest::foo_1 @20, time: 110
wWinMain @13, time: 0
Для себя я ставил задачи:
- добиться минимального оверхэда кода (потому как это вспомогательный код, а не основной)
- добиться максимальной эффективности и удобства использования программистом
Код работает, но поскольку я не ассемблерщик меня терзают смутные сомнения:
- возможно ли что улучшить или сократить без потери функциональности, удобства, производительности ?
- возможно ли избавиться от RAII обертки CLog без потери удобства автовызова StepOut?
- оправдано ли использование/таскание по всюду ссылки на CStackTrace? Возможно быстрее будет с указателем?
- мне не нравится использовать макросы, но без них получается очень много мусора, который будет скрывать полезный код. Варианты?
Другими словами, чтобы вам хотелось оптимизировать/исправить в данном коде ?
PS. Отдельная просьба советы сходить на github/stackoverflow/google, взять вот этот log-trace проект и не лохматить бабушку оставить при себе
--------------------------------------------------------------
o(O_O)o