1 // Copyright (c) 2011-2012 Ryan Prichard
3 // Permission is hereby granted, free of charge, to any person obtaining a copy
4 // of this software and associated documentation files (the "Software"), to
5 // deal in the Software without restriction, including without limitation the
6 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 // sell copies of the Software, and to permit persons to whom the Software is
8 // furnished to do so, subject to the following conditions:
10 // The above copyright notice and this permission notice shall be included in
11 // all copies or substantial portions of the Software.
13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 #include "DebugClient.h"
31 #include "winpty_snprintf.h"
33 const wchar_t *const kPipeName = L"\\\\.\\pipe\\DebugServer";
35 void *volatile g_debugConfig;
39 // It would be easy to accidentally trample on the Windows LastError value
40 // by adding logging/debugging code. Ensure that can't happen by saving and
41 // restoring the value. This saving and restoring doesn't happen along the
43 class PreserveLastError {
45 PreserveLastError() : m_lastError(GetLastError()) {}
46 ~PreserveLastError() { SetLastError(m_lastError); }
51 } // anonymous namespace
53 static void sendToDebugServer(const char *message)
55 HANDLE tracePipe = INVALID_HANDLE_VALUE;
58 // The default impersonation level is SECURITY_IMPERSONATION, which allows
59 // a sufficiently authorized named pipe server to impersonate the client.
60 // There's no need for impersonation in this debugging system, so reduce
61 // the impersonation level to SECURITY_IDENTIFICATION, which allows a
62 // server to merely identify us.
63 tracePipe = CreateFileW(
65 GENERIC_READ | GENERIC_WRITE,
66 0, NULL, OPEN_EXISTING,
67 SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION,
69 } while (tracePipe == INVALID_HANDLE_VALUE &&
70 GetLastError() == ERROR_PIPE_BUSY &&
71 WaitNamedPipeW(kPipeName, NMPWAIT_WAIT_FOREVER));
73 if (tracePipe != INVALID_HANDLE_VALUE) {
74 DWORD newMode = PIPE_READMODE_MESSAGE;
75 SetNamedPipeHandleState(tracePipe, &newMode, NULL, NULL);
78 TransactNamedPipe(tracePipe,
79 const_cast<char*>(message), strlen(message),
80 response, sizeof(response), &actual, NULL);
81 CloseHandle(tracePipe);
85 // Get the current UTC time as milliseconds from the epoch (ignoring leap
86 // seconds). Use the Unix epoch for consistency with DebugClient.py. There
87 // are 134774 days between 1601-01-01 (the Win32 epoch) and 1970-01-01 (the
89 static long long unixTimeMillis()
92 GetSystemTimeAsFileTime(&fileTime);
93 long long msTime = (((long long)fileTime.dwHighDateTime << 32) +
94 fileTime.dwLowDateTime) / 10000;
95 return msTime - 134774LL * 24 * 3600 * 1000;
98 static const char *getDebugConfig()
100 if (g_debugConfig == NULL) {
101 PreserveLastError preserve;
102 const int bufSize = 256;
105 GetEnvironmentVariableA("WINPTY_DEBUG", buf, bufSize);
106 if (actualSize == 0 || actualSize >= static_cast<DWORD>(bufSize)) {
109 const size_t len = strlen(buf) + 1;
110 char *newConfig = new char[len];
111 std::copy(buf, buf + len, newConfig);
112 void *oldValue = InterlockedCompareExchangePointer(
113 &g_debugConfig, newConfig, NULL);
114 if (oldValue != NULL) {
118 return static_cast<const char*>(g_debugConfig);
121 bool isTracingEnabled()
123 static bool disabled, enabled;
126 } else if (enabled) {
129 // Recognize WINPTY_DEBUG=1 for backwards compatibility.
130 PreserveLastError preserve;
131 bool value = hasDebugFlag("trace") || hasDebugFlag("1");
138 bool hasDebugFlag(const char *flag)
140 if (strchr(flag, ',') != NULL) {
141 trace("INTERNAL ERROR: hasDebugFlag flag has comma: '%s'", flag);
144 const char *const configCStr = getDebugConfig();
145 if (configCStr[0] == '\0') {
148 PreserveLastError preserve;
149 std::string config(configCStr);
150 std::string flagStr(flag);
151 config = "," + config + ",";
152 flagStr = "," + flagStr + ",";
153 return config.find(flagStr) != std::string::npos;
156 void trace(const char *format, ...)
158 if (!isTracingEnabled())
161 PreserveLastError preserve;
165 va_start(ap, format);
166 winpty_vsnprintf(message, format, ap);
167 message[sizeof(message) - 1] = '\0';
170 const int currentTime = (int)(unixTimeMillis() % (100000 * 1000));
172 char moduleName[1024];
173 moduleName[0] = '\0';
174 GetModuleFileNameA(NULL, moduleName, sizeof(moduleName));
175 const char *baseName = strrchr(moduleName, '\\');
176 baseName = (baseName != NULL) ? baseName + 1 : moduleName;
178 char fullMessage[1024];
179 winpty_snprintf(fullMessage,
180 "[%05d.%03d %s,p%04d,t%04d]: %s",
181 currentTime / 1000, currentTime % 1000,
182 baseName, (int)GetCurrentProcessId(), (int)GetCurrentThreadId(),
184 fullMessage[sizeof(fullMessage) - 1] = '\0';
186 sendToDebugServer(fullMessage);