Lib
QOLを高める
debug.cpp
Go to the documentation of this file.
1 #include "stdafx.h"
2 #include <windows.h>
3 #include "include/debug.h"
4 #include "include/util.h"
5 
6 namespace yappy {
7 namespace debug {
8 
9 namespace {
10 
11 bool s_debugOut = false;
12 bool s_consoleOut = false;
13 bool s_fileOut = false;
14 
15 HANDLE s_hFile = INVALID_HANDLE_VALUE;
16 
17 } // namespace
18 
19 bool enableDebugOutput() noexcept
20 {
21  s_debugOut = true;
22  return s_debugOut;
23 }
24 
25 bool enableConsoleOutput() noexcept
26 {
27  if (s_consoleOut) {
28  return s_consoleOut;
29  }
30  BOOL ret = ::AllocConsole();
31  if (ret) {
32  HANDLE hOut = ::GetStdHandle(STD_OUTPUT_HANDLE);
33  COORD maxSize = GetLargestConsoleWindowSize(hOut);
34  COORD bufSize = { 80, maxSize.Y * 10 };
35  SMALL_RECT rect = { 0, 0, bufSize.X - 1, maxSize.Y * 3 / 4 };
36  SetConsoleScreenBufferSize(hOut, bufSize);
37  SetConsoleWindowInfo(hOut, TRUE, &rect);
38  s_consoleOut = true;
39  }
40  return s_consoleOut;
41 }
42 
43 bool enableFileOutput(const wchar_t *fileName) noexcept
44 {
45  if (s_fileOut) {
46  return s_fileOut;
47  }
48  HANDLE h = ::CreateFile(fileName, GENERIC_WRITE, FILE_SHARE_READ, nullptr,
49  CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
50  if (h != INVALID_HANDLE_VALUE) {
51  s_fileOut = true;
52  s_hFile = h;
53  }
54  return s_fileOut;
55 }
56 
57 void shutdownDebugOutput() noexcept
58 {
59  s_debugOut = false;
60 
61  if (s_consoleOut) {
62  ::FreeConsole();
63  }
64  s_consoleOut = false;
65 
66  if (s_hFile != INVALID_HANDLE_VALUE) {
67  ::CloseHandle(s_hFile);
68  }
69  s_fileOut = false;
70 }
71 
72 
73 void write(const wchar_t *str, bool newline) noexcept
74 {
75  // Debug Out
76  if (s_debugOut) {
77  ::OutputDebugString(str);
78  if (newline) {
79  ::OutputDebugString(L"\n");
80  }
81  }
82  // Console Out
83  if (s_consoleOut) {
84  HANDLE hOut = ::GetStdHandle(STD_OUTPUT_HANDLE);
85  DWORD len = static_cast<DWORD>(wcslen(str));
86  DWORD written = 0;
87  ::WriteConsole(hOut, str, len, &written, NULL);
88  if (newline) {
89  ::WriteConsole(hOut, L"\n", 1, &written, NULL);
90  }
91  }
92  // File Out
93  if (s_fileOut) {
94  auto mbstr = util::wc2utf8(str);
95  DWORD written = 0;
96  ::WriteFile(s_hFile, mbstr.get(), static_cast<DWORD>(strlen(mbstr.get())), &written, nullptr);
97  if (newline) {
98  ::WriteFile(s_hFile, "\n", 1, &written, nullptr);
99  }
100  }
101 }
102 
103 void writef(const wchar_t *fmt, ...) noexcept
104 {
105  va_list args;
106  va_start(args, fmt);
107  wchar_t buf[1024];
108  _vsnwprintf_s(buf, _TRUNCATE, fmt, args);
109  va_end(args);
110 
111  write(buf, true);
112 }
113 
114 void writef(const char *fmt, ...) noexcept
115 {
116  va_list args;
117  va_start(args, fmt);
118  char buf[1024];
119  vsnprintf_s(buf, _TRUNCATE, fmt, args);
120  va_end(args);
121 
122  write(buf, true);
123 }
124 
125 void writef_nonl(const wchar_t *fmt, ...) noexcept
126 {
127  va_list args;
128  va_start(args, fmt);
129  wchar_t buf[1024];
130  _vsnwprintf_s(buf, _TRUNCATE, fmt, args);
131  va_end(args);
132 
133  write(buf, false);
134 }
135 
136 void writef_nonl(const char *fmt, ...) noexcept
137 {
138  va_list args;
139  va_start(args, fmt);
140  char buf[1024];
141  vsnprintf_s(buf, _TRUNCATE, fmt, args);
142  va_end(args);
143 
144  write(buf, false);
145 }
146 
147 } // namespace debug
148 
149 namespace trace {
150 
151 namespace {
152 
154 
155 const size_t LINE_DATA_SIZE = 128;
156 struct LineBuffer {
157  LARGE_INTEGER counter;
158  uint32_t number;
159  char msg[LINE_DATA_SIZE - sizeof(LARGE_INTEGER) - sizeof(uint32_t)];
160 };
161 static_assert(sizeof(LineBuffer) == LINE_DATA_SIZE, "invalid struct def");
162 
163 struct VirtualDeleter {
164  void operator()(void *p)
165  {
166  BOOL ret = ::VirtualFree(p, 0, MEM_RELEASE);
167  if (!ret) {
168  debug::writeLine(L"VirtualFree() failed");
169  }
170  }
171 };
172 
173 std::unique_ptr<LineBuffer[], VirtualDeleter> s_buf;
174 size_t s_size;
175 uint32_t s_number;
176 
177 } // namespace
178 
179 void initialize(size_t bufsize)
180 {
181  void *p = ::VirtualAlloc(nullptr, bufsize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
182  checkWin32Result(p != nullptr, "VirtualAlloc() failed");
183  s_buf.reset(static_cast<LineBuffer *>(p));
184  s_size = bufsize / sizeof(LineBuffer);
185  s_number = 0;
186 
187  std::memset(s_buf.get(), 0, bufsize);
188 }
189 
190 void output()
191 {
192  wchar_t tempPath[MAX_PATH];
193  DWORD dret = GetTempPath(MAX_PATH, tempPath);
194  checkWin32Result(dret != 0, "GetTempPath() failed");
195  wchar_t filePath[MAX_PATH];
196  UINT uret = GetTempFileName(tempPath, L"trc", 0, filePath);
197  checkWin32Result(uret != 0, "GetTempFileName() failed");
198  HANDLE tmpFile = CreateFile(filePath, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
199  checkWin32Result(tmpFile != INVALID_HANDLE_VALUE, "GetTempFileName() failed");
200  util::HandlePtr hFile(tmpFile);
201 
202  LARGE_INTEGER freq = { 0 };
203  ::QueryPerformanceFrequency(&freq);
204 
205  LARGE_INTEGER prevCount = { 0 };
206  for (size_t i = 0; i < s_size; i++) {
207  size_t ind = (s_number + i) % s_size;
208  const LineBuffer &line = s_buf[ind];
209  if (line.counter.QuadPart == 0) {
210  continue;
211  }
212  auto writeFunc = [&hFile](const char *str) {
213  DWORD len = static_cast<DWORD>(std::strlen(str));
214  DWORD writeSize = 0;
215  BOOL bret = WriteFile(hFile.get(), str, len, &writeSize, nullptr);
216  checkWin32Result(bret != 0, "WriteFile() failed");
217  };
218  {
219  double sec = 0.0;
220  if (prevCount.QuadPart != 0) {
221  sec = static_cast<double>(line.counter.QuadPart - prevCount.QuadPart) / freq.QuadPart;
222  }
223  prevCount = s_buf[ind].counter;
224  char buf[64];
225  ::sprintf_s(buf, "[%08x +%07.4fms] ", s_buf[ind].number, sec * 1e3);
226  writeFunc(buf);
227  }
228  writeFunc(line.msg);
229  writeFunc("\r\n");
230  }
231 
232  // close file
233  hFile.reset();
234  // open with notepad
235  HINSTANCE hret = ::ShellExecute(
236  nullptr, nullptr, L"notepad", filePath, nullptr, SW_NORMAL);
237  checkWin32Result(reinterpret_cast<ULONG_PTR>(hret) > 32, "ShellExecute() failed");
238 }
239 
240 void write(const char *str)
241 {
242  LineBuffer &line = s_buf[s_number % s_size];
243  ::QueryPerformanceCounter(&line.counter);
244  line.number = s_number;
245 
246  ::strncpy_s(line.msg, str, sizeof(line.msg) - 1);
247  s_number++;
248 }
249 
250 } // namespace trace
251 } // namespace yappy
std::unique_ptr< char[]> wc2utf8(const wchar_t *in)
Wide char to UTF-8.
Definition: util.h:105
bool enableDebugOutput() noexcept
Enables writing to OutputDebugString().
Definition: debug.cpp:19
void write(const wchar_t *str, bool newline) noexcept
Write debug string.
Definition: debug.cpp:73
const char * str
Definition: input.cpp:197
uint32_t number
Definition: debug.cpp:158
Debug utilities.
void writef(const wchar_t *fmt,...) noexcept
Write debug message using format string like printf.
Definition: debug.cpp:103
void initialize(size_t bufsize)
Initialize trace buffer.
Definition: debug.cpp:179
void shutdownDebugOutput() noexcept
Flush buffers and free resources.
Definition: debug.cpp:57
Definition: config.cpp:6
void writeLine(const wchar_t *str=L"") noexcept
Write debug string and new line.
Definition: debug.h:64
std::unique_ptr< HANDLE, HandleDeleter > HandlePtr
unique_ptr of HANDLE with HandleDeleter.
Definition: util.h:64
void output()
Output to a temporary file and open it with notepad.
Definition: debug.cpp:190
Utilities.
char msg[LINE_DATA_SIZE-sizeof(LARGE_INTEGER)-sizeof(uint32_t)]
Definition: debug.cpp:159
bool enableFileOutput(const wchar_t *fileName) noexcept
Enables writing to a file.
Definition: debug.cpp:43
bool enableConsoleOutput() noexcept
Shows a console window and enables writing to it.
Definition: debug.cpp:25
void checkWin32Result(bool cond, const std::string &msg)
Definition: exceptions.h:50
LARGE_INTEGER counter
Definition: debug.cpp:157
void writef_nonl(const wchar_t *fmt,...) noexcept
Write debug message using format string like printf. (No new line)
Definition: debug.cpp:125