blob: 168f49487154c3e069cd987a1131e666b2f048f6 [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2012-2019 Google, Inc. All rights reserved.
* Copyright (c) 2003-2008 VMware, Inc. All rights reserved.
* **********************************************************/
/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of VMware, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
/*
* DRview.c
*
* command-line tool for determining what is running under DR
*
* (c) araksha, inc. all rights reserved
*/
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <tchar.h>
/* The order here now matters: share.h needs to be the first to include
* globals_shared.h, as it has to set HOT_PATCHING_INTERFACE first;
* but ntdll.h now includes globals_shared.h, so we put it second.
* But, processes.h duplicates _VM_COUNTERS from ntdll.h but can handle
* going 2nd but not first: so it must go last.
*/
#include "share.h"
#include "ntdll.h"
#include "processes.h"
#define NAME "DR"
int
procwalk();
void
dllwalk();
void
usage()
{
fprintf(stderr, "Usage:\n");
fprintf(stderr,
"DRview [-help] [-pid n] [-exe name] [-listdr] [-listall] [-listdlls] "
"[-showdlls] [-nopid] [-no32] [-out file] [-cmdline] [-showmem] [-showtime] "
"[-nobuildnum] [-qname strip] [-noqnames] [-hot_patch] [-s n] [-tillidle] "
"[-idlecpu c] [-showmemfreq f] [-idleafter s] [-v]\n");
exit(1);
}
void
help()
{
fprintf(stderr, "Options:\n");
fprintf(stderr, " -pid n\t\t\tdisplays whether the process is injected into\n");
fprintf(stderr, " -exe name\t\tfinds all processes whose executable matches\n");
fprintf(stderr, "\t\t\t'name', shows whether injected into\n");
fprintf(stderr, " -listdr\t\tlist all processes injected into\n");
fprintf(
stderr,
" -listall\t\tlist all processes on the system, show whether injected_into\n");
fprintf(stderr,
" -listdlls\t\tlist all DLLs [short] for a specific pid or executable\n");
fprintf(stderr,
" -showdlls\t\tlist all DLLs [long] for a specific pid or executable\n");
fprintf(
stderr,
" -nopid\t\t\tdoes not display PIDs of processes (useful for expect files)\n");
#ifdef X64
fprintf(stderr, " -no32\t\t\tdoes not display whether 32-bit\n");
#endif
fprintf(stderr, " -onlypid\t\tonly shows PID\n");
fprintf(stderr, " -out file\t\toutput to file instead of stdout\n");
fprintf(stderr, " -cmdline\t\tshow process command lines\n");
fprintf(stderr, " -showmem\t\tshow memory stats\n");
fprintf(stderr,
" -showtime\t\tshow scheduled time for each process (needs -showmem)\n");
fprintf(stderr, " -showstats\t\tshow internal stats\n");
fprintf(stderr,
" -nobuildnum\t\tdoes not display build number of SC (useful for expect "
"files)\n");
fprintf(stderr, " -qname strip\t\tshow qualified names; set strip to 0 or 1\n");
fprintf(stderr, " -noqnames\t\tdon\'t show qualified names\n");
fprintf(stderr, " -hot_patch\t\tshow hot patch status\n");
fprintf(stderr,
" -s n\t\t\tsample every n millis (default: 500ms, e.g. 1s=1000, 5min=300000 "
"5*60*1000)\n");
fprintf(stderr, " -tillidle\t\tsample until idle (default: < 3%% cpu for 3s)\n");
fprintf(stderr,
" -idlecpu c\t\tconsider < c%% cpu utilization idle (default: 3%%)\n");
/* Sometimes -listall -showmem causes lsass %cpu to remain high, so
* we provide an option to sample frequently but query lsass less frequently.
*/
fprintf(stderr,
" -showmemfreq f\t\tfor -tillidle -showmem, show -showmem every f samples "
"(default: 1)\n");
fprintf(stderr,
" -idleafter s\t\tflag machine is idle after s seconds (default: 3s)\n");
fprintf(stderr, " -v\t\t\tdisplay version information\n\n");
exit(1);
}
BOOL listdr = FALSE, listall = FALSE, nopid = FALSE, no32 = FALSE, onlypid = FALSE,
listdlls = FALSE, showdlls = FALSE, qname = FALSE, strip = FALSE, noqnames = FALSE;
cmdline = FALSE, hotp = FALSE, showtime = FALSE, sampling = FALSE, tillidle = FALSE,
idle = FALSE, skip = FALSE, showmem = FALSE, showbuild = TRUE,
showstats = FALSE;
uint pid = 0;
uint millis = 0;
uint idlecpu = 0;
uint showmemfreq = 1;
uint flag_after_ms = 0;
char *exe = NULL, *outf = NULL;
process_id_t save_pid = 0;
WCHAR save_module[MAX_PATH];
WCHAR wexe[MAX_PATH];
FILE *fp;
LONGLONG total_user = 0;
LONGLONG total_kernel = 0;
#define MAX_CMDLINE 2048
#define BUFFER_SIZE_BYTES(buf) sizeof(buf)
#define BUFFER_SIZE_ELEMENTS(buf) (BUFFER_SIZE_BYTES(buf) / sizeof(buf[0]))
#define BUFFER_LAST_ELEMENT(buf) buf[BUFFER_SIZE_ELEMENTS(buf) - 1]
#define NULL_TERMINATE_BUFFER(buf) BUFFER_LAST_ELEMENT(buf) = 0
#define NULL_TERMINATE_SIZED_BUFFER(buf, size) (buf)[(size)-1] = 0
static void
generate_process_name(process_info_t *pi, WCHAR *name_buf /* OUT */,
uint name_buf_length /* elements */)
{
WCHAR qual_name[MAX_CMDLINE];
WCHAR qual_args[MAX_CMDLINE];
int res;
BOOL use_args = FALSE;
/* hack: we assume only need qualified names for these hardcoded apps
* FIXME: read registry to see whether need qualification
*/
if (wcsicmp(pi->ProcessName, L"svchost.exe") == 0 ||
wcsicmp(pi->ProcessName, L"msiexec.exe") == 0 ||
wcsicmp(pi->ProcessName, L"tomcat.exe") == 0 ||
wcsicmp(pi->ProcessName, L"dllhost.exe") == 0) {
/* use qualified names */
res = get_process_cmdline(pi->ProcessID, qual_name,
BUFFER_SIZE_ELEMENTS(qual_name));
NULL_TERMINATE_BUFFER(qual_name);
if (res == ERROR_SUCCESS) {
if (get_commandline_qualifier(
qual_name, qual_args, BUFFER_SIZE_ELEMENTS(qual_args),
/* hack: we assume we only strip svchost,
* and we also strip dllhost here to
* fit more of the GUI in and avoid the
* "Processid" string.
* FIXME: read from registry.
*/
(wcsicmp(pi->ProcessName, L"svchost.exe") != 0 &&
wcsicmp(pi->ProcessName, L"dllhost.exe") != 0))) {
NULL_TERMINATE_BUFFER(qual_args);
use_args = TRUE;
} /* else not an error, just no args. e.g.: plain sqlservr.exe */
} else {
/* this is an error => notify user */
_snwprintf(qual_args, BUFFER_SIZE_ELEMENTS(qual_args), L"<error>");
NULL_TERMINATE_BUFFER(qual_args);
use_args = TRUE;
}
}
if (use_args) {
_snwprintf(name_buf, name_buf_length, L"%s-%s", pi->ProcessName, qual_args);
} else {
_snwprintf(name_buf, name_buf_length, L"%s", pi->ProcessName);
}
NULL_TERMINATE_SIZED_BUFFER(name_buf, name_buf_length);
}
static void
print_mem_stats(process_info_t *pi, char reschar, int version)
{
WCHAR qual_name[MAX_CMDLINE];
int cpu = -1, user = -1;
LONGLONG wallclock_time = get_system_time() - pi->CreateTime.QuadPart;
LONGLONG scheduled_time = pi->UserTime.QuadPart + pi->KernelTime.QuadPart;
static LONGLONG firstproc_time = 0;
if (wallclock_time != (LONGLONG)0) {
cpu = (int)((100 * scheduled_time) / wallclock_time);
}
if (scheduled_time != (LONGLONG)0) {
user = (int)((100 * pi->UserTime.QuadPart) / scheduled_time);
}
/* Total user and kernel time scheduled for all processes. Don't include idle
* process, and if -skip option is specified don't include DRview.exe */
if (wcsicmp(pi->ProcessName, L"") != 0 &&
(wcsicmp(pi->ProcessName, L"drview.exe") != 0 || !skip)) {
total_user += pi->UserTime.QuadPart;
total_kernel += pi->KernelTime.QuadPart;
}
/* CreateTime.QuadPart is a counter since 1916. Both idle process and
* System have create time of 0, so report create time, in ms, relative to
* smss.exe */
if (wcsicmp(pi->ProcessName, L"") == 0 || wcsicmp(pi->ProcessName, L"system") == 0 ||
wcsicmp(pi->ProcessName, L"smss.exe") == 0) {
firstproc_time = pi->CreateTime.QuadPart;
}
generate_process_name(pi, qual_name, BUFFER_SIZE_ELEMENTS(qual_name));
/* single line best so can line up columns and process output easily */
fprintf(fp,
"%-23.23S %5d %c %5d %2d%% %3d%% %5d %3d %7zd %7zd %8zd %7zd %7zd %7zd %7d "
"%5zd %5zd "
"%5zd %5zd %5d",
qual_name, pi->ProcessID, reschar, version, cpu, user, pi->HandleCount,
pi->ThreadCount, pi->VmCounters.PeakVirtualSize / 1024,
pi->VmCounters.VirtualSize / 1024, pi->VmCounters.PeakPagefileUsage / 1024,
pi->VmCounters.PagefileUsage / 1024, /* aka Private */
pi->VmCounters.PeakWorkingSetSize / 1024,
pi->VmCounters.WorkingSetSize / 1024, pi->VmCounters.PageFaultCount,
pi->VmCounters.QuotaPeakPagedPoolUsage / 1024,
pi->VmCounters.QuotaPagedPoolUsage / 1024,
pi->VmCounters.QuotaPeakNonPagedPoolUsage / 1024,
pi->VmCounters.QuotaNonPagedPoolUsage / 1024, pi->InheritedFromProcessID);
if (showtime) {
/* QuadPart is in ticks 1 tick = 100 nano secs. In ms = n * 100/(1000*1000) */
fprintf(fp, " %15I64d %15I64d %15I64d", (pi->UserTime.QuadPart / 10000),
(pi->KernelTime.QuadPart / 10000),
((pi->CreateTime.QuadPart - firstproc_time) / 10000));
}
fprintf(fp, "\n");
}
int
main(int argc, char **argv)
{
int nprocs, argidx = 1;
if (argc < 2)
usage();
while (argidx < argc) {
if (!strcmp(argv[argidx], "-help")) {
help();
} else if (!strcmp(argv[argidx], "-pid") || !strcmp(argv[argidx], "-p")) {
if (argidx + 1 >= argc)
usage();
pid = atoi(argv[++argidx]);
} else if (!strcmp(argv[argidx], "-exe")) {
if (argidx + 1 >= argc)
usage();
exe = argv[++argidx];
_snwprintf(wexe, MAX_PATH, L"%S", exe);
wexe[MAX_PATH - 1] = L'\0';
} else if (!strcmp(argv[argidx], "-listall")) {
listall = TRUE;
} else if (!strcmp(argv[argidx], "-listdr")) {
listdr = TRUE;
} else if (!strcmp(argv[argidx], "-listdlls")) {
listdlls = TRUE;
} else if (!strcmp(argv[argidx], "-showdlls")) {
listdlls = TRUE;
showdlls = TRUE;
} else if (!strcmp(argv[argidx], "-nopid")) {
nopid = TRUE;
} else if (!strcmp(argv[argidx], "-no32")) {
no32 = TRUE;
} else if (!strcmp(argv[argidx], "-onlypid")) {
onlypid = TRUE;
} else if (!strcmp(argv[argidx], "-out")) {
if (argidx + 1 >= argc)
usage();
outf = argv[++argidx];
} else if (!strcmp(argv[argidx], "-cmdline")) {
cmdline = TRUE;
} else if (!strcmp(argv[argidx], "-showstats")) {
showstats = TRUE;
} else if (!strcmp(argv[argidx], "-showmem")) {
showmem = TRUE;
} else if (!strcmp(argv[argidx], "-showtime")) {
if (!showmem)
usage();
showtime = TRUE;
} else if (!strcmp(argv[argidx], "-nobuildnum")) {
showbuild = FALSE;
} else if (!strcmp(argv[argidx], "-qname")) {
if (argidx + 1 >= argc)
usage();
qname = TRUE;
strip = atoi(argv[++argidx]);
} else if (!strcmp(argv[argidx], "-noqnames")) {
noqnames = TRUE;
} else if (!strcmp(argv[argidx], "-hot_patch")) {
hotp = TRUE;
} else if (!strcmp(argv[argidx], "-s")) {
millis = (uint)atoi(argv[argidx + 1]);
if (millis == 0) {
millis = 500;
} else {
argidx++;
}
sampling = TRUE;
} else if (!strcmp(argv[argidx], "-tillidle")) {
millis = (millis <= 0) ? 500 : millis;
idlecpu = (idlecpu <= 0) ? 3 : idlecpu;
showmemfreq = (showmemfreq <= 1) ? 1 : showmemfreq;
flag_after_ms = (flag_after_ms <= 0) ? 3000 : flag_after_ms;
tillidle = TRUE;
sampling = TRUE;
} else if (!strcmp(argv[argidx], "-idlecpu")) {
if (argidx + 1 >= argc)
usage();
idlecpu = (uint)atoi(argv[++argidx]);
idlecpu = (idlecpu <= 0) ? 3 : idlecpu;
millis = (millis <= 0) ? 500 : millis;
flag_after_ms = (flag_after_ms <= 0) ? 3000 : flag_after_ms;
tillidle = TRUE;
sampling = TRUE;
} else if (!strcmp(argv[argidx], "-showmemfreq")) {
if (argidx + 1 >= argc)
usage();
showmemfreq = (uint)atoi(argv[++argidx]);
showmemfreq = (showmemfreq <= 1) ? 1 : showmemfreq;
showmem = TRUE;
tillidle = TRUE;
sampling = TRUE;
} else if (!strcmp(argv[argidx], "-idleafter")) {
if (argidx + 1 >= argc)
usage();
flag_after_ms = (uint)atoi(argv[++argidx]);
flag_after_ms = (flag_after_ms <= 0) ? 3000 : flag_after_ms;
idlecpu = (idlecpu <= 0) ? 3 : idlecpu;
millis = (millis <= 0) ? 500 : millis;
sampling = TRUE;
tillidle = TRUE;
} else if (!strcmp(argv[argidx], "-skip")) {
/* internal option: skip drview's contribution to total scheduled time */
skip = TRUE;
} else if (!strcmp(argv[argidx], "-v")) {
#if defined(BUILD_NUMBER) && defined(VERSION_NUMBER)
printf("drview.exe version %s -- build %d\n", STRINGIFY(VERSION_NUMBER),
BUILD_NUMBER);
#elif defined(BUILD_NUMBER)
printf("drview.exe custom build %d -- %s\n", BUILD_NUMBER, __DATE__);
#else
printf("drview.exe custom build -- %s, %s\n", __DATE__, __TIME__);
#endif
exit(0);
} else {
fprintf(stderr, "Unknown option: %s\n", argv[argidx]);
usage();
}
argidx++;
}
if (listdlls && !pid && !exe && !listall && !listdr) {
fprintf(stderr,
"-listdlls option should be combined with a specific -pid, -exe, -listdr "
"or -listall option\n");
usage();
}
if (outf) {
fp = fopen(outf, "w");
if (fp == NULL) {
fprintf(stderr, "Error opening %s for output.\n", outf);
exit(-1);
}
} else
fp = stdout;
/* Invoke DLL routine when -listdlls combined with either -listall or -pid or -exe */
if (listdlls && (listall || pid || exe || listdr)) {
dllwalk();
} else {
do {
nprocs = procwalk();
fflush(fp);
if (sampling)
Sleep(millis);
/* FIXME: looping infinitely, make sure we are not leaking anything */
} while (sampling && !idle);
}
if (outf)
fclose(fp);
return nprocs;
}
int count;
/* returns FALSE if status is effectively disabled, otherwise returns
* TRUE (indicating status should be displayed. */
BOOL
get_status_string(char *buf, UINT maxchars, hotp_inject_status_t status,
hotp_policy_mode_t mode)
{
char *statptr = NULL;
char *modeptr = NULL;
if (status == HOTP_INJECT_NO_MATCH && mode == HOTP_MODE_OFF)
return FALSE;
switch (status) {
case HOTP_INJECT_ERROR: statptr = "Inject Error!"; break;
case HOTP_INJECT_PROTECT: statptr = "Injected Protector"; break;
case HOTP_INJECT_DETECT: statptr = "Injected Detector"; break;
case HOTP_INJECT_IN_PROGRESS: statptr = "Injection in progress"; break;
case HOTP_INJECT_PENDING: statptr = "Inject point not yet executed"; break;
case HOTP_INJECT_NO_MATCH: statptr = "Not matched"; break;
case HOTP_INJECT_OFF:
/* not currently used. */
default: statptr = NULL;
}
switch (mode) {
case HOTP_MODE_OFF: modeptr = "Off"; break;
case HOTP_MODE_DETECT: modeptr = "Detect"; break;
case HOTP_MODE_PROTECT: modeptr = "Protect"; break;
default: modeptr = NULL;
}
if (statptr == NULL || modeptr == NULL) {
_snprintf(buf, maxchars, "[Status ERROR: Status=%d, Mode=%d]", status, mode);
} else {
_snprintf(buf, maxchars, "%s [%s]", modeptr, statptr);
}
buf[maxchars - 1] = '\0';
return TRUE;
}
BOOL
pw_callback(process_info_t *pi, void **param)
{
char *resstr;
char reschar;
int res;
WCHAR buf[MAX_CMDLINE];
DWORD version;
BOOL under_dr;
WCHAR qual_name[MAX_CMDLINE];
if (exe)
generate_process_name(pi, qual_name, BUFFER_SIZE_ELEMENTS(qual_name));
if ((pid && pi->ProcessID == pid) ||
(exe && (!wcsicmp(wexe, pi->ProcessName) || !wcsicmp(wexe, qual_name))) ||
listall || listdr) {
version = -1;
res = under_dynamorio_ex(pi->ProcessID, &version);
switch (res) {
case DLL_PROFILE:
resstr = NAME " profile";
reschar = 'P';
break;
case DLL_RELEASE:
resstr = NAME " release";
reschar = 'R';
break;
case DLL_DEBUG:
resstr = NAME " debug";
reschar = 'D';
break;
case DLL_CUSTOM:
resstr = NAME " custom";
reschar = 'C';
break;
case DLL_NONE:
resstr = "native";
reschar = 'N';
break;
case DLL_UNKNOWN:
default: resstr = "<error>"; reschar = '?';
}
under_dr = !(res == DLL_NONE || res == DLL_UNKNOWN);
if (!listdr || under_dr) {
if (!nopid && !showmem) {
if (onlypid)
fprintf(fp, "%d\n", (DWORD)pi->ProcessID);
else
fprintf(fp, "PID %d, ", (DWORD)pi->ProcessID);
}
if (!showmem && !onlypid) {
WCHAR qual_name[MAX_CMDLINE];
WCHAR *name_to_use = pi->ProcessName;
#ifdef X64
HANDLE hproc =
OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pi->ProcessID);
if (is_wow64(hproc)) {
if (!no32)
fprintf(fp, "32-bit, ");
/* FIXME: currently x64 process can't see 32-bit
* drmarker
*/
resstr = "<unknown>";
}
CloseHandle(hproc);
#endif
if (!noqnames) {
generate_process_name(pi, qual_name, BUFFER_SIZE_ELEMENTS(qual_name));
name_to_use = qual_name;
}
fprintf(fp, "Process %S, ", name_to_use);
if (version == -1 || !showbuild)
fprintf(fp, "running %s\n", resstr);
else
fprintf(fp, "running %s (build %d)\n", resstr, version);
}
if (cmdline) {
res = get_process_cmdline(pi->ProcessID, buf, BUFFER_SIZE_ELEMENTS(buf));
NULL_TERMINATE_BUFFER(buf);
if (res == ERROR_SUCCESS) {
fprintf(fp, "\tCmdline: %S\n", buf);
} else {
/* acquiring SeDebugPrivilege requires being admin */
if (res == ERROR_NOT_ALL_ASSIGNED)
fprintf(fp, "\t<Re-run as administrator for cmdline>\n");
else
fprintf(fp, "\t<Cmdline err %d>\n", res);
}
}
if (qname) {
WCHAR cmdline[MAX_CMDLINE];
res = get_process_cmdline(pi->ProcessID, cmdline,
BUFFER_SIZE_ELEMENTS(cmdline));
NULL_TERMINATE_BUFFER(cmdline);
if (res == ERROR_SUCCESS) {
if (!get_commandline_qualifier(cmdline, buf,
BUFFER_SIZE_ELEMENTS(buf), !strip))
buf[0] = L'\0'; /* no args */
NULL_TERMINATE_BUFFER(buf);
}
if (res == ERROR_SUCCESS)
fprintf(fp, "\tQname: %S%s%S\n", pi->ProcessName,
buf[0] == L'\0' ? "" : "-", buf);
else
fprintf(fp, "\t<Qname err %d>\n", res);
}
if (under_dr && hotp) {
hotp_policy_status_table_t *status_tbl = NULL;
res = get_hotp_status(pi->ProcessID, &status_tbl);
if (res == ERROR_SUCCESS) {
uint j;
hotp_policy_status_t *cur;
fprintf(fp, "\tHotpatching:\n");
for (j = 0; j < status_tbl->num_policies; j++) {
char status_buf[MAX_PATH];
cur = &(status_tbl->policy_status_array[j]);
if (get_status_string(status_buf, MAX_PATH, cur->inject_status,
cur->mode))
fprintf(fp, "\t Patch %s: %s\n", cur->policy_id, status_buf);
}
} else if (res == ERROR_DRMARKER_ERROR) {
fprintf(fp, "\tHot Patching Not Enabled\n");
} else {
fprintf(fp, "\t<Hotpatch Query Error %d>\n", res);
}
}
if (under_dr && showstats) {
dr_statistics_t *stats = get_dynamorio_stats(pi->ProcessID);
if (stats != NULL) {
uint i;
fprintf(fp, "\t%.*s\n",
(int)BUFFER_SIZE_ELEMENTS(stats->process_name),
stats->process_name);
for (i = 0; i < stats->num_stats; i++) {
fprintf(fp, "\t%*.*s :%9zd\n",
(int)BUFFER_SIZE_ELEMENTS(stats->stats[i].name),
(int)BUFFER_SIZE_ELEMENTS(stats->stats[i].name),
stats->stats[i].name, stats->stats[i].value);
}
}
free_dynamorio_stats(stats);
}
if (showmem) {
print_mem_stats(pi, reschar, version);
}
count++;
}
}
return TRUE;
}
BOOL
dllw_callback(module_info_t *mi, void **param)
{
char *resstr;
char reschar;
int res;
DWORD version;
BOOL exe_match = (exe && wcsstr(mi->BaseDllName, wexe)) ? TRUE : FALSE;
BOOL exe_present =
(wcsstr(mi->BaseDllName, L".exe") || wcsstr(mi->BaseDllName, L".EXE")) ? TRUE
: FALSE;
/* -listdlls option is set && any of below 3 conditions hold true.
(1) -pid value is set and current threadID matches.
(2) -exe is set and all DLLs with matching threadIDs (a fake comparison) since
WINDOWS provides no other handle. (3) -listall option is set
*/
if (listdlls &&
((pid && mi->ProcessID == pid) ||
((exe_match ||
(exe && save_pid == mi->ProcessID &&
(!exe_present || wcscmp(save_module, mi->BaseDllName) == 0)))) ||
(listall || listdr))) {
version = -1;
res = under_dynamorio_ex(mi->ProcessID, &version);
switch (res) {
case DLL_PROFILE:
resstr = NAME " profile";
reschar = 'P';
break;
case DLL_RELEASE:
resstr = NAME " release";
reschar = 'R';
break;
case DLL_DEBUG:
resstr = NAME " debug";
reschar = 'D';
break;
case DLL_CUSTOM:
resstr = NAME " custom";
reschar = 'C';
break;
case DLL_NONE:
resstr = "native";
reschar = 'N';
break;
case DLL_UNKNOWN:
default: resstr = "<error>"; reschar = '?';
}
save_pid = mi->ProcessID;
if (!(listdr && (res == DLL_NONE || res == DLL_UNKNOWN))) {
if (wcsstr(mi->BaseDllName, L".exe") || wcsstr(mi->BaseDllName, L".EXE")) {
wcsncpy(save_module, mi->BaseDllName,
sizeof(save_module) / sizeof(save_module[0]));
save_module[(sizeof(save_module) / sizeof(save_module[0])) - 1] = L'\0';
if (!nopid && !showmem)
fprintf(fp, "\n\nPID " PIDFMT, mi->ProcessID);
if (!showmem) {
if (version == -1 || !showbuild) {
fprintf(fp, "\t\tProcess %S, running %s\n", mi->BaseDllName,
resstr);
} else {
fprintf(fp, "\t\tProcess %S, running %s (build %d)\n",
mi->BaseDllName, resstr, version);
}
}
count = 0;
} else {
if (!showmem) {
if (showdlls) {
/* long format */
fprintf(fp, " %p-%p %-16S Stamp=%x Count=%d\n %S\n",
mi->BaseAddress,
(char *)mi->BaseAddress + mi->SizeOfImage,
mi->BaseDllName, mi->TimeDateStamp, mi->LoadCount,
mi->FullDllName);
} else {
fprintf(fp, "\t%-16S", mi->BaseDllName);
count++;
if ((count % 3) == 0) {
fprintf(fp, "\n");
count = 0;
}
}
}
}
}
}
return TRUE;
}
int
procwalk()
{
static int pwalk_per = 0;
uint system_load = get_system_load(sampling);
if (tillidle) {
static uint idle_for_ms = 0;
idle_for_ms = (system_load < idlecpu) ? idle_for_ms + millis : 0;
idle = (idle_for_ms >= flag_after_ms);
}
count = 0;
if (showmem) {
SYSTEM_PERFORMANCE_INFORMATION sperf_info;
if (get_system_performance_info(&sperf_info)) {
fprintf(fp, "System committed memory (KB): %d / %d peak %d\n",
/* in pages so x4==KB */
4 * sperf_info.TotalCommittedPages, 4 * sperf_info.TotalCommitLimit,
4 * sperf_info.PeakCommitment);
/* FIXME: add physical memory, kernel memory, etc. */
}
fprintf(fp, "System load: %d%%\t\tUptime: %lu ms\n", system_load, get_uptime());
/* %user reaches 100% so we give it the extra column over %cpu */
fprintf(fp,
"%-23s %5s %7s %3s %4s %5s %3s %7s %7s %8s %7s %7s %7s %7s %5s %5s %5s "
"%5s %5s",
"Name-Qualification", "PID", "DR Bld", "CPU", "User", "Hndl", "Thr",
"PVSz", "VSz", "PPriv", "Priv", "PWSS", "WSS", "Fault", "PPage", "Page",
"PNonP", "NonP", "PPID");
if (showtime)
fprintf(fp, " %15s %15s %15s", "User Time(ms)", "Kernel Time(ms)",
"Create Time(ms)");
fprintf(fp, "\n");
}
if (pwalk_per == 0 || pwalk_per % showmemfreq == 0) {
process_walk(&pw_callback, NULL);
if (!count)
fprintf(fp, "No such process found.\n");
else {
if (showmem && showtime) {
fprintf(fp, "Total scheduled user/kernel time: %I64d/%I64d ms\n",
total_user / 10000, total_kernel / 10000);
}
}
}
pwalk_per++;
total_user = 0;
total_kernel = 0;
return count;
}
void
dllwalk()
{
count = 0;
dll_walk_all(&dllw_callback, NULL);
}