diff options
Diffstat (limited to 'samples/tracebld/tracebld.cpp')
-rw-r--r-- | samples/tracebld/tracebld.cpp | 569 |
1 files changed, 569 insertions, 0 deletions
diff --git a/samples/tracebld/tracebld.cpp b/samples/tracebld/tracebld.cpp new file mode 100644 index 0000000..7573b4a --- /dev/null +++ b/samples/tracebld/tracebld.cpp @@ -0,0 +1,569 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Detours Test Program (tracebld.cpp of tracebld.exe) +// +// Microsoft Research Detours Package +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +#include <windows.h> +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#pragma warning(push) +#if _MSC_VER > 1400 +#pragma warning(disable:6102 6103) // /analyze warnings +#endif +#include <strsafe.h> +#pragma warning(pop) +#include <detours.h> +#include "tracebld.h" + +#if (_MSC_VER < 1299) +typedef ULONG * PULONG_PTR; +typedef ULONG ULONG_PTR; +typedef LONG * PLONG_PTR; +typedef LONG LONG_PTR; +#endif + +////////////////////////////////////////////////////////////////////////////// +#pragma warning(disable:4127) // Many of our asserts are constants. + +#define ASSERT_ALWAYS(x) \ + do { \ + if (!(x)) { \ + AssertMessage(#x, __FILE__, __LINE__); \ + DebugBreak(); \ + } \ + } while (0) + +#ifndef NDEBUG +#define ASSERT(x) ASSERT_ALWAYS(x) +#else +#define ASSERT(x) +#endif + +#define UNUSED(c) (c) = (c) + +////////////////////////////////////////////////////////////////////////////// + +enum { + CLIENT_AWAITING_PIPE_ACCEPT = 0x21, + CLIENT_AWAITING_PIPE_DATA = 0x22, +}; + +typedef struct _CLIENT : OVERLAPPED +{ + HANDLE hPipe; + LONG nClient; + HANDLE hFile; + BOOL fAwaitingAccept; + PVOID Zero; + TBLOG_MESSAGE Message; + + BOOL LogMessage(PTBLOG_MESSAGE pMessage, DWORD nBytes); + BOOL LogMessageV(PCHAR pszMsg, ...); +} CLIENT, *PCLIENT; + +////////////////////////////////////////////////////////////////////////////// +// +CHAR s_szLogFile[MAX_PATH]; +CHAR s_szPipe[MAX_PATH]; +LONG s_nActiveClients = 0; +LONG s_nTotalClients = 0; +LONGLONG s_llStartTime; +BOOL s_fVerbose = FALSE; +TBLOG_PAYLOAD s_Payload; + +////////////////////////////////////////////////////////////////////////////// +// +VOID MyErrExit(PCSTR pszMsg) +{ + DWORD error = GetLastError(); + + fprintf(stderr, "TRACEBLD: Error %ld in %s.\n", error, pszMsg); + fflush(stderr); + exit(1); +} + +////////////////////////////////////////////////////////////////////////////// +// +BOOL CLIENT::LogMessageV(PCHAR pszMsg, ...) +{ + DWORD cbWritten = 0; + CHAR szBuf[1024]; + PCHAR pcchEnd = szBuf + ARRAYSIZE(szBuf) - 2; + PCHAR pcchCur = szBuf; + HRESULT hr; + + va_list args; + va_start(args, pszMsg); + hr = StringCchVPrintfExA(pcchCur, pcchEnd - pcchCur, + &pcchCur, NULL, STRSAFE_NULL_ON_FAILURE, + pszMsg, args); + va_end(args); + if (FAILED(hr)) { + goto cleanup; + } + + hr = StringCchPrintfExA(pcchCur, szBuf + (ARRAYSIZE(szBuf)) - pcchCur, + &pcchCur, NULL, STRSAFE_NULL_ON_FAILURE, + "\n"); + + cleanup: + WriteFile(hFile, szBuf, (DWORD)(pcchCur - szBuf), &cbWritten, NULL); + return TRUE; +} + +BOOL CLIENT::LogMessage(PTBLOG_MESSAGE pMessage, DWORD nBytes) +{ + // Sanity check the size of the message. + // + if (nBytes > pMessage->nBytes) { + nBytes = pMessage->nBytes; + } + if (nBytes >= sizeof(*pMessage)) { + nBytes = sizeof(*pMessage) - 1; + } + + // Don't log message if there isn't and message text. + // + DWORD cbWrite = nBytes - offsetof(TBLOG_MESSAGE, szMessage); + if (cbWrite <= 0 ) { + return TRUE; + } + + if (s_fVerbose) { + printf("[%s]", pMessage->szMessage); + } + + DWORD cbWritten = 0; + WriteFile(hFile, pMessage->szMessage, cbWrite, &cbWritten, NULL); + return TRUE; +} + +BOOL CloseConnection(PCLIENT pClient) +{ + InterlockedDecrement(&s_nActiveClients); + if (pClient != NULL) { + if (pClient->hPipe != INVALID_HANDLE_VALUE) { + //FlushFileBuffers(pClient->hPipe); + if (!DisconnectNamedPipe(pClient->hPipe)) { + DWORD error = GetLastError(); + pClient->LogMessageV("<!-- Error %d in DisconnectNamedPipe. -->\n", error); + } + CloseHandle(pClient->hPipe); + pClient->hPipe = INVALID_HANDLE_VALUE; + } + if (pClient->hFile != INVALID_HANDLE_VALUE) { + CloseHandle(pClient->hFile); + pClient->hFile = INVALID_HANDLE_VALUE; + } + GlobalFree(pClient); + pClient = NULL; + } + return TRUE; +} + +// Creates a pipe instance and initiate an accept request. +// +PCLIENT CreatePipeConnection(HANDLE hCompletionPort, LONG nClient) +{ + HANDLE hPipe = CreateNamedPipeA(s_szPipe, // pipe name + PIPE_ACCESS_INBOUND | // read-only access + FILE_FLAG_OVERLAPPED, // overlapped mode + PIPE_TYPE_MESSAGE | // message-type pipe + PIPE_READMODE_MESSAGE | // message read mode + PIPE_WAIT, // blocking mode + PIPE_UNLIMITED_INSTANCES, // unlimited instances + 0, // output buffer size + 0, // input buffer size + 20000, // client time-out + NULL); // no security attributes + if (hPipe == INVALID_HANDLE_VALUE) { + MyErrExit("CreateNamedPipe"); + } + + // Allocate the client data structure. + // + PCLIENT pClient = (PCLIENT) GlobalAlloc(GPTR, sizeof(CLIENT)); + if (pClient == NULL) { + MyErrExit("GlobalAlloc pClient"); + } + + CHAR szLogFile[MAX_PATH]; + StringCchPrintfA(szLogFile, ARRAYSIZE(szLogFile), "%s.%08d.xml", s_szLogFile, nClient); + + ZeroMemory(pClient, sizeof(*pClient)); + pClient->hPipe = hPipe; + pClient->nClient = nClient; + pClient->fAwaitingAccept = TRUE; + pClient->hFile = CreateFileA(szLogFile, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | + FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + if (pClient->hFile == INVALID_HANDLE_VALUE) { + fprintf(stderr, "TRACEBLD: Error opening output file: %s: %ld\n\n", + szLogFile, GetLastError()); + fflush(stderr); + MyErrExit("CreateFile"); + } + + // Associate file with our complietion port. + // + if (!CreateIoCompletionPort(pClient->hPipe, hCompletionPort, (ULONG_PTR)pClient, 0)) { + MyErrExit("CreateIoComplietionPort pClient"); + } + + if (!ConnectNamedPipe(hPipe, pClient)) { + DWORD error = GetLastError(); + + if (error == ERROR_IO_PENDING) { + return NULL; + } + if (error == ERROR_PIPE_CONNECTED) { +#if 0 + pClient->LogMessageV("<!-- ConnectNamedPipe client already connected. -->"); +#endif + pClient->fAwaitingAccept = FALSE; + } + else if (error != ERROR_IO_PENDING && + error != ERROR_PIPE_LISTENING) { + + MyErrExit("ConnectNamedPipe"); + } + } + else { + fprintf(stderr, "*** ConnectNamedPipe accepted immediately.\n"); +#if 0 + pClient->LogMessageV("<!-- ConnectNamedPipe accepted immediately. -->"); +#endif + pClient->fAwaitingAccept = FALSE; + } + return pClient; +} + +BOOL DoRead(PCLIENT pClient) +{ + SetLastError(NO_ERROR); + DWORD nBytes = 0; + BOOL b = ReadFile(pClient->hPipe, &pClient->Message, sizeof(pClient->Message), + &nBytes, pClient); + + DWORD error = GetLastError(); + + if (b && error == NO_ERROR) { + return TRUE; + } + if (error == ERROR_BROKEN_PIPE) { + pClient->LogMessageV("<!-- **** ReadFile 002 *** ERROR_BROKEN_PIPE [%d] -->\n", nBytes); + CloseConnection(pClient); + return TRUE; + } + else if (error == ERROR_INVALID_HANDLE) { + // ? + pClient->LogMessageV("<!-- **** ReadFile 002 *** ERROR_INVALID_HANDLE -->\n"); + // I have no idea why this happens. Our remedy is to drop the connection. + return TRUE; + } + else if (error != ERROR_IO_PENDING) { + if (b) { + pClient->LogMessageV("<!-- **** ReadFile 002 succeeded: %d -->\n", error); + } + else { + pClient->LogMessageV("<!-- **** ReadFile 002 failed: %d -->\n", error); + } + CloseConnection(pClient); + } + return TRUE; +} + +DWORD WINAPI WorkerThread(LPVOID pvVoid) +{ + PCLIENT pClient; + BOOL b; + LPOVERLAPPED lpo; + DWORD nBytes; + HANDLE hCompletionPort = (HANDLE)pvVoid; + + for (BOOL fKeepLooping = TRUE; fKeepLooping;) { + pClient = NULL; + lpo = NULL; + nBytes = 0; + b = GetQueuedCompletionStatus(hCompletionPort, + &nBytes, (PULONG_PTR)&pClient, &lpo, INFINITE); + + if (!b) { + if (pClient) { + if (GetLastError() == ERROR_BROKEN_PIPE) { + pClient->LogMessageV("<!-- Client closed pipe. -->"); + } + else { + pClient->LogMessageV("<!-- *** GetQueuedCompletionStatus failed %d -->", + GetLastError()); + } + CloseConnection(pClient); + } + continue; + } + + if (pClient->fAwaitingAccept) { + BOOL fAgain = TRUE; + while (fAgain) { + LONG nClient = InterlockedIncrement(&s_nTotalClients); + InterlockedIncrement(&s_nActiveClients); + pClient->fAwaitingAccept = FALSE; + + PCLIENT pNew = CreatePipeConnection(hCompletionPort, nClient); + + fAgain = FALSE; + if (pNew != NULL) { + fAgain = !pNew->fAwaitingAccept; + DoRead(pNew); + } + } + } + else { + if (nBytes <= offsetof(TBLOG_MESSAGE, szMessage)) { + pClient->LogMessageV("</t:Process>\n"); + CloseConnection(pClient); + continue; + } + pClient->LogMessage(&pClient->Message, nBytes); + } + + DoRead(pClient); + } + return 0; +} + +BOOL CreateWorkers(HANDLE hCompletionPort) +{ + DWORD dwThread; + HANDLE hThread; + DWORD i; + SYSTEM_INFO SystemInfo; + + GetSystemInfo(&SystemInfo); + + for (i = 0; i < 1; i++) { + hThread = CreateThread(NULL, 0, WorkerThread, hCompletionPort, 0, &dwThread); + if (!hThread) { + MyErrExit("CreateThread WorkerThread"); + // Unreachable: return FALSE; + } + CloseHandle(hThread); + } + return TRUE; +} + +DWORD CopyEnvironment(PWCHAR pwzzOut, PCWSTR pwzzIn) +{ + PCWSTR pwzzBeg = pwzzOut; + while (*pwzzIn) { + while (*pwzzIn) { + *pwzzOut++ = *pwzzIn++; + } + *pwzzOut++ = *pwzzIn++; // Copy zero. + } + *pwzzOut++ = '\0'; // Add last zero. + + return (DWORD)(pwzzOut - pwzzBeg); +} + +////////////////////////////////////////////////////////////////////////////// +// +DWORD main(int argc, char **argv) +{ + HANDLE hCompletionPort; + BOOL fNeedHelp = FALSE; + WCHAR wzzDrop[1024] = L"build\0nmake\0"; + + GetSystemTimeAsFileTime((FILETIME *)&s_llStartTime); + StringCchPrintfA(s_szPipe, ARRAYSIZE(s_szPipe), "%s.%d", TBLOG_PIPE_NAME, GetCurrentProcessId()); + + int arg = 1; + for (; arg < argc && (argv[arg][0] == '-' || argv[arg][0] == '/'); arg++) { + CHAR *argn = argv[arg] + 1; + CHAR *argp = argn; + while (*argp && *argp != ':' && *argp != '=') { + argp++; + } + if (*argp == ':' || *argp == '=') { + *argp++ = '\0'; + } + + switch (argn[0]) { + + case 'd': // Drop Processes + case 'D': + if (*argp) { + PWCHAR pwz = wzzDrop; + while (*argp) { + if (*argp == ';') { + *pwz++ = '\0'; + } + else { + *pwz++ = *argp++; + } + } + *pwz++ = '\0'; + *pwz = '\0'; + } + case 'o': // Output file. + case 'O': + StringCchCopyA(s_szLogFile, ARRAYSIZE(s_szLogFile), argp); + break; + + case 'v': // Verbose + case 'V': + s_fVerbose = TRUE; + break; + + case '?': // Help. + fNeedHelp = TRUE; + break; + + default: + fNeedHelp = TRUE; + printf("TRACEBLD: Bad argument: %s:%s\n", argn, argp); + break; + } + } + + if (arg >= argc) { + fNeedHelp = TRUE; + } + + if (fNeedHelp) { + printf("Usage:\n" + " tracebld [options] command {command arguments}\n" + "Options:\n" + " /o:file Log all events to the output files.\n" + " /? Display this help message.\n" + "Summary:\n" + " Runs the build commands and figures out which files have dependencies..\n" + "\n"); + exit(9001); + } + + // Create the completion port. + hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 0); + if (hCompletionPort == NULL) { + MyErrExit("CreateIoCompletionPort"); + } + + // Create completion port worker threads. + // + CreateWorkers(hCompletionPort); + CreatePipeConnection(hCompletionPort, 0); + + printf("TRACEBLD: Ready for clients. Press Ctrl-C to stop.\n"); + + /////////////////////////////////////////////////////////// Validate DLLs. + // + CHAR szTmpPath[MAX_PATH]; + CHAR szExePath[MAX_PATH]; + CHAR szDllPath[MAX_PATH]; + PCHAR pszFilePart = NULL; + + if (!GetModuleFileNameA(NULL, szTmpPath, ARRAYSIZE(szTmpPath))) { + printf("TRACEBLD: Couldn't retreive exe name.\n"); + return 9002; + } + if (!GetFullPathNameA(szTmpPath, ARRAYSIZE(szExePath), szExePath, &pszFilePart) || + pszFilePart == NULL) { + printf("TRACEBLD: Error: %s is not a valid path name..\n", szTmpPath); + return 9002; + } + + StringCchCopyA(pszFilePart, szExePath + ARRAYSIZE(szExePath) - pszFilePart, + "trcbld" DETOURS_STRINGIFY(DETOURS_BITS) ".dll"); + StringCchCopyA(szDllPath, ARRAYSIZE(szDllPath), szExePath); + + ////////////////////////////////////////////////////////////////////////// + STARTUPINFOA si; + PROCESS_INFORMATION pi; + CHAR szCommand[2048]; + CHAR szExe[MAX_PATH]; + CHAR szFullExe[MAX_PATH] = "\0"; + PCHAR pszFileExe = NULL; + + ZeroMemory(&si, sizeof(si)); + ZeroMemory(&pi, sizeof(pi)); + si.cb = sizeof(si); + + szCommand[0] = L'\0'; + + StringCchCopyA(szExe, sizeof(szExe), argv[arg]); + for (; arg < argc; arg++) { + if (strchr(argv[arg], ' ') != NULL || strchr(argv[arg], '\t') != NULL) { + StringCchCatA(szCommand, sizeof(szCommand), "\""); + StringCchCatA(szCommand, sizeof(szCommand), argv[arg]); + StringCchCatA(szCommand, sizeof(szCommand), "\""); + } + else { + StringCchCatA(szCommand, sizeof(szCommand), argv[arg]); + } + + if (arg + 1 < argc) { + StringCchCatA(szCommand, sizeof(szCommand), " "); + } + } + printf("TRACEBLD: Starting: `%s'\n", szCommand); + printf("TRACEBLD: with `%s'\n", szDllPath); + fflush(stdout); + + DWORD dwFlags = CREATE_DEFAULT_ERROR_MODE | CREATE_SUSPENDED; + + SetLastError(0); + SearchPathA(NULL, szExe, ".exe", ARRAYSIZE(szFullExe), szFullExe, &pszFileExe); + + + if (!DetourCreateProcessWithDllExA(szFullExe[0] ? szFullExe : NULL, szCommand, + NULL, NULL, TRUE, dwFlags, NULL, NULL, + &si, &pi, szDllPath, NULL)) { + printf("TRACEBLD: DetourCreateProcessWithDllEx failed: %ld\n", GetLastError()); + ExitProcess(9007); + } + + ZeroMemory(&s_Payload, sizeof(s_Payload)); + s_Payload.nParentProcessId = GetCurrentProcessId(); + s_Payload.nTraceProcessId = GetCurrentProcessId(); + s_Payload.nGeneology = 1; + s_Payload.rGeneology[0] = 0; + StringCchCopyW(s_Payload.wzStdin, ARRAYSIZE(s_Payload.wzStdin), L"\\\\.\\CONIN$"); + StringCchCopyW(s_Payload.wzStdout, ARRAYSIZE(s_Payload.wzStdout), L"\\\\.\\CONOUT$"); + StringCchCopyW(s_Payload.wzStderr, ARRAYSIZE(s_Payload.wzStderr), L"\\\\.\\CONOUT$"); + StringCchCopyW(s_Payload.wzParents, ARRAYSIZE(s_Payload.wzParents), L""); + CopyEnvironment(s_Payload.wzzDrop, wzzDrop); + LPWCH pwStrings = GetEnvironmentStringsW(); + CopyEnvironment(s_Payload.wzzEnvironment, pwStrings); + FreeEnvironmentStringsW(pwStrings); + + if (!DetourCopyPayloadToProcess(pi.hProcess, s_guidTrace, + &s_Payload, sizeof(s_Payload))) { + printf("TRACEBLD: DetourCopyPayloadToProcess failed: %ld\n", GetLastError()); + ExitProcess(9008); + } + + ResumeThread(pi.hThread); + + WaitForSingleObject(pi.hProcess, INFINITE); + + DWORD dwResult = 0; + if (!GetExitCodeProcess(pi.hProcess, &dwResult)) { + printf("TRACEBLD: GetExitCodeProcess failed: %ld\n", GetLastError()); + return 9008; + } + + printf("TRACEBLD: %ld processes.\n", s_nTotalClients); + + return dwResult; +} +// +////////////////////////////////////////////////////////////////////////////// |