///////////////////////////////////////////////////////////////////////////// // // threads.cpp - Extension to find Singularity threads. // // Copyright (c) Microsoft Corporation. All rights reserved. // #include "singx86.h" ULONG64 stoppedThreads; ULONG64 blockedThreads; ULONG64 runnableThreads; ULONG64 unblockedThreads; ULONG64 idleThreads; CHAR * GetQueueName(ULONG64 queue) { if (stoppedThreads == 0) { stoppedThreads = GetStaticPointer("nt!Scheduler::stoppedThreads"); blockedThreads = GetStaticPointer("nt!Scheduler::blockedThreads"); runnableThreads = GetStaticPointer("nt!Scheduler::runnableThreads"); unblockedThreads = GetStaticPointer("nt!Scheduler::unblockedThreads"); idleThreads = GetStaticPointer("nt!Scheduler::idleThreads"); ExtVerb("Queues: stopped=%p blocked=%p runnable=%p unblocked=%p idle=%p\n", stoppedThreads, blockedThreads, runnableThreads, unblockedThreads, idleThreads); } if (queue == stoppedThreads) { return "stopped"; } if (queue == blockedThreads) { return "blocked"; } if (queue == runnableThreads) { return "runnable"; } if (queue == unblockedThreads) { return "unblocked"; } if (queue == idleThreads) { return "idle"; } return "running"; } HRESULT DumpThread(ULONG64 threadaddr, bool stack, bool fullstack, bool detail, bool blocked) { HRESULT status = S_OK; if (threadaddr == 0) { return S_FALSE; } Thread thread; ThreadEntry entry; EXT_CHECK(ThreadStruct.Read(threadaddr, &thread)); EXT_CHECK(ThreadEntryStruct.Read(thread.schedulerEntry, &entry)); char *which = GetQueueName(entry.queue); char offQueueMarker = (entry.queue != NULL) ? ' ' : '*'; if ((stack) || (fullstack)) { ExtOut("%c thread=%p { tid=%03x pid=%03x eip=%p, ebp=%p, esp=%p %02x }\n", offQueueMarker, threadaddr, (ULONG)(thread.context.threadIndex == 0xffff ? 0xfff : thread.context.threadIndex), (ULONG)(thread.context.processId == 0xffff ? 0xfff : thread.context.processId), thread.context.eip, thread.context.ebp, thread.context.esp, (ULONG)thread.context.gcStates); if (fullstack) { DEBUG_STACK_FRAME frames[30]; ULONG filled = 0; status = g_ExtControl->GetStackTrace(thread.context.ebp, thread.context.esp, thread.context.eip, frames, arrayof(frames), &filled); if (status == S_OK) { g_ExtClient->SetOutputLinePrefix(" "); g_ExtControl->OutputStackTrace(DEBUG_OUTPUT_NORMAL, frames, filled, detail ? (DEBUG_STACK_FRAME_ADDRESSES | DEBUG_STACK_COLUMN_NAMES | DEBUG_STACK_SOURCE_LINE) : 0); } } else { DEBUG_STACK_FRAME frames[10]; ULONG filled = 0; status = g_ExtControl->GetStackTrace(thread.context.ebp, thread.context.esp, thread.context.eip, frames, arrayof(frames), &filled); if (status == S_OK) { g_ExtClient->SetOutputLinePrefix(" "); g_ExtControl->OutputStackTrace(DEBUG_OUTPUT_NORMAL, frames, filled, detail ? (DEBUG_STACK_FRAME_ADDRESSES | DEBUG_STACK_COLUMN_NAMES | DEBUG_STACK_SOURCE_LINE) : 0); } } } else { CHAR name[512]; ULONG64 displacement = 0; status = g_ExtSymbols->GetNameByOffset(thread.context.eip, name, arrayof(name), NULL, &displacement); if (status == S_OK) { ExtOut("%c thread=%p { tid=%03x pid=%03x eip=%p, ebp=%p, esp=%p %02x } %s+%I64d\n", offQueueMarker, threadaddr, (ULONG)(thread.context.threadIndex == 0xffff ? 0xfff : thread.context.threadIndex), (ULONG)(thread.context.processId == 0xffff ? 0xfff : thread.context.processId), thread.context.eip, thread.context.ebp, thread.context.esp, (ULONG)thread.context.gcStates, name, (LONG64)displacement); } else { ExtOut("%c thread=%p { tid=%03x pid=%03x eip=%p, ebp=%p, esp=%p %02x }\n", offQueueMarker, threadaddr, (ULONG)(thread.context.threadIndex == 0xffff ? 0xfff : thread.context.threadIndex), (ULONG)(thread.context.processId == 0xffff ? 0xfff : thread.context.processId), thread.context.eip, thread.context.ebp, thread.context.esp, (ULONG)thread.context.gcStates); } } if (blocked) { if (entry.queue != NULL) { } } if (blocked) { ExtOut(" { %-8.8s blocked=%d count=%d until=%16I64x queue=%p entry=%p}\n", which, (ULONG)thread.blocked, (ULONG)thread.blockedOnCount, thread.blockedUntil, entry.queue, thread.schedulerEntry); } Exit: return status; } EXT_DECL(threads) // Defines: PDEBUG_CLIENT Client, PCSTR args { EXT_ENTER(); // Defines: HRESULT status = S_OK; bool stack = false; bool fullstack = false; bool detail = false; bool blocked = false; while (*args != '\0') { // skip whitespace while (*args == ' ' || *args == '\t') { args++; } // process argument if (*args == '-' || *args == '/') { args++; switch (*args++) { case 'd': // detail case 'D': detail = !detail; break; case 'f': // fullstack case 'F': fullstack = !fullstack; break; case 's': // stack case 'S': stack = !stack; break; case 'b': // Blocked information case 'B': blocked = !blocked; break; case '?': // Help case 'h': case 'H': status = S_FALSE; goto Exit; } while (*args != ' ') { args++; } } else { break; } } ULONG pointerSize = (g_ExtControl->IsPointer64Bit() == S_OK) ? 8 : 4; ULONG64 address = 0; ULONG64 threads = 0; ULONG type = 0; ULONG subtype = 0; ULONG64 module = 0; EXT_CHECK(g_ExtSymbols->GetOffsetByName("nt!Thread::threadTable", &address)); EXT_CHECK(g_ExtSymbols->GetOffsetTypeId(address, &type, &module)); EXT_CHECK(g_ExtData->ReadPointersVirtual(1, address, &threads)); ExtVerb("threadTable: %p\n", threads); CHAR name[512]; EXT_CHECK(g_ExtSymbols->GetTypeName(module, type, name, arrayof(name), NULL)); ExtVerb(" threadTable type: %s\n", name); int len = strlen(name); if (len > 3 && name[len-3] == '[' && name[len-2] == ']' && name[len-1] == '*') { name[len-3] = '\0'; EXT_CHECK(g_ExtSymbols->GetTypeId(module, name, &subtype)); EXT_CHECK(g_ExtSymbols->GetTypeName(module, subtype, name, arrayof(name), NULL)); ExtVerb(" threadTable[] type: %s\n", name); } ULONG lengthOffset = 0; EXT_CHECK(g_ExtSymbols->GetFieldOffset(module, type, "overall_length", &lengthOffset)); ULONG valuesOffset = 0; EXT_CHECK(g_ExtSymbols->GetFieldOffset(module, type, "values", &valuesOffset)); ULONG length = 0; EXT_CHECK(g_ExtData->ReadVirtual(threads + lengthOffset, &length, sizeof(length), NULL)); ExtOut("threadTable: %p [maximum %d entries]\n", threads, length); CHAR prefix[256]; EXT_CHECK(client->GetOutputLinePrefix(prefix, arrayof(prefix), NULL)); for (ULONG id = 0; id < length; id++) { ULONG64 thread = 0; EXT_CHECK(g_ExtData->ReadPointersVirtual(1, threads + valuesOffset + id * pointerSize, &thread)); if (thread != 0) { DumpThread(thread, stack, fullstack, detail, blocked); g_ExtClient->SetOutputLinePrefix(prefix); } } EXT_LEAVE(); // Macro includes: return status; }