aboutsummaryrefslogtreecommitdiffhomepage
path: root/samples/tracebld/tracebld.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'samples/tracebld/tracebld.cpp')
-rw-r--r--samples/tracebld/tracebld.cpp569
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;
+}
+//
+//////////////////////////////////////////////////////////////////////////////