/* (C) 2003-2007 Willem Jan Hengeveld * Web: http://www.xs4all.nl/~itsme/ * http://wiki.xda-developers.com/ * * $Id: pps.cpp 1921 2008-07-31 17:08:31Z itsme $ * * module maskbit = 1<<((base>>25)-1) * * perldo s/\w+/sprintf("%08lx %s", 1<<((hex($&)>>25)-1), $&)/e * * slot 0 base 0x00000000 : current process * slot 1 base 0x02000000 : dlls * slot 2 base 0x04000000 -> maskbit 0x00000002 - filesys.exe * slot 3 base 0x06000000 -> maskbit 0x00000004 - device.exe * .. * slot 32 base 0x40000000 -> maskbit 0x80000000 * * slot ** base 0xc2000000 -> maskbit 0x00000001 kernel * * todo: * pps -n procname ... DONE * should do the same as 'pps -h HANDLE' * * pps -t -h HANDLE * pps -m -h HANDLE ... DONE * should list info only for proc HANDLE * */ #include #include #include #include #include #define MEASUREMENT_MSEC 200 #include "ItsUtils.h" #include "dllversion.h" #include "debug.h" #include "ptrutils.h" #include "args.h" class ProcessTimes { public: DWORD nThreads; DWORD tKernel; DWORD tUser; }; typedef std::map ProcessTimeMap; typedef std::map ProcessInfoMap; typedef std::map ThreadSummaryInfoMap; void PrintProcessList(const ProcessInfoMap &pinfo, const ProcessTimeMap &ptm0, const ProcessTimeMap &ptm1, DWORD measurementtime, bool bVerbose); bool GetProcessInfo(bool bIncludeHeap, ProcessInfoMap &pinfo); bool GetProcessTimes(ProcessTimeMap &ptimes, DWORD &tQuery); bool ITGetThreadUsageList(bool resolve_modulenames, DWORD& tQuery, ThreadSummaryInfoMap& tinfo); HANDLE ITGetProcessHandle(char *szProcessName); void PrintThreadList(const ProcessInfoMap &pinfo, const ThreadSummaryInfoMap& tsim0, const ThreadSummaryInfoMap& tsim1, DWORD measurementtime, bool bVerbose, HANDLE hProcFilter); void PrintThreadEntry(const CEPROCESSENTRY* pe, const ThreadSummaryInfo& ti, const ThreadSummaryInfo* ti0, const ThreadSummaryInfo* ti1, DWORD measurementtime, bool bVerbose); void PrintContext(); void PrintThreadInfo(HANDLE hThread, DWORD tMeasure); bool ITGetThreadTimes(HANDLE hThread, FILETIME *tCreate, FILETIME *tExit, FILETIME *tKernel, FILETIME *tUser); BOOL CalcFiletimeDiff(const FILETIME *tFirst, const FILETIME *tLast, FILETIME *tDiff); DWORD CalcPromile(FILETIME *tA, FILETIME *tB); DWORD GetProcessSectionSlot(HANDLE hProc); typedef std::vector ModuleEntryList; bool ITGetModuleList(bool bDirectRead, ModuleEntryList &pinfo); void PrintModuleList(bool bVerbose, const ModuleEntryList& modlist, DWORD dwProcMask); void usage() { printf("(C) 2003-2008 Willem jan Hengeveld itsme@xs4all.nl\n"); printf("Usage: pps [options]\n"); printf(" -t : list detailed thread info\n"); printf(" -s msec : measurement interval\n"); printf(" -s0 : give total cpu time, instead of percentage\n"); printf(" -h : include heap information (slow)\n"); printf(" -m : list all modules ( or for process )\n"); printf(" -p HANDLE : restrict modules list to process\n"); printf(" -n PROCNAM: restrict modules list to process\n"); printf(" -v : verbose\n"); } int main( int argc, char *argv[]) { DebugStdOut(); bool bVerbose= false; HANDLE hThread= INVALID_HANDLE_VALUE; HANDLE hProcess= INVALID_HANDLE_VALUE; char *szProcessName= NULL; DWORD tMeasure= MEASUREMENT_MSEC; bool bListAllThreads= false; bool bIncludeHeap= false; bool bListModules= false; int argsfound=0; for (int i=1 ; i>25)-1); } } if (bListModules) { // ! abusing -h = bIncludeHeap as alternate mode for modlist ModuleEntryList modlist; if (!ITGetModuleList(dwProcMask==0xFFFFFFFF ? bIncludeHeap : true, modlist)) return 1; PrintModuleList(bIncludeHeap, modlist, dwProcMask); return 0; } if (bListAllThreads) { ProcessInfoMap pinfo; ThreadSummaryInfoMap tsim0, tsim1; DWORD tQuery0, tQuery1; if (!GetProcessInfo(false, pinfo)) return 1; if (!ITGetThreadUsageList(false, tQuery0, tsim0)) return 1; if (tMeasure) { Sleep(tMeasure); if (!ITGetThreadUsageList(true, tQuery1, tsim1)) return 1; PrintThreadList(pinfo, tsim0, tsim1, tQuery1-tQuery0, bVerbose, hProcess); } else { PrintThreadList(pinfo, tsim0, tsim1, 0, bVerbose, hProcess); } } else if (hThread!=INVALID_HANDLE_VALUE) PrintThreadInfo(hThread, tMeasure); else { ProcessInfoMap pinfo; ProcessTimeMap tm0, tm1; DWORD tQuery0, tQuery1; if (!GetProcessInfo(bIncludeHeap, pinfo)) return 1; if (!GetProcessTimes(tm0, tQuery0)) return 1; if (tMeasure) { Sleep(tMeasure); if (!GetProcessTimes(tm1, tQuery1)) return 1; PrintProcessList(pinfo, tm0, tm1, tQuery1-tQuery0, bVerbose); } else { PrintProcessList(pinfo, tm0, tm1, 0, bVerbose); } } CeRapiUninit(); return 0; } void PrintContext() { DWORD outsize=0; GetContextResult *outbuf=NULL; HRESULT res= ItsutilsInvoke(L"ITGetContext", 0, NULL, &outsize, (BYTE**)&outbuf); printf("result=%08lx buf=%08lx size=%08lx\n", res, outbuf, outsize); if (res==0 && outbuf!=NULL) { printf("pid=%08lx ph=%08lx tid=%08lx th=%08lx trust.caller=%d trust.cur=%d\n", outbuf->dwProcessId, outbuf->hProcess, outbuf->dwThreadId, outbuf->hThread, outbuf->dwCurrentTrust, outbuf->dwCallerTrust); printf("cmd: %ls\n", outbuf->wszCmdLine); printf("memory: kernel=%10d used=%10d free=%10d\n", outbuf->dwKernelMemory, outbuf->dwMemoryUsed, outbuf->dwMemoryFree); LocalFree(outbuf); } printf("\n"); } const ProcessTimes *GetTime(const ProcessTimeMap &tmap, HANDLE h) { ProcessTimeMap::const_iterator i= tmap.find(h); if (i==tmap.end()) return NULL; return &(*i).second; } void PrintProcessList(const ProcessInfoMap &pinfo, const ProcessTimeMap &tm0, const ProcessTimeMap &tm1, DWORD measurementtime, bool bVerbose) { double pmUserTotal=0; double pmKernelTotal=0; DWORD dwMemTotal=0; DWORD dwTotalThreads=0; if (measurementtime==0) { if (bVerbose) printf("%-8s %3s %-8s %-8s %10s %10s %7s %s\n", "handle", "n", "base", "bit", "kern", "user", "heap", "exe"); else printf("%-8s %3s %-8s %10s %10s %7s %s\n", "handle", "n", "base", "kern", "user", "heap", "exe"); } else { if (bVerbose) printf("%-8s %3s %-8s %-8s %5s %5s %7s %s\n", "handle", "n", "base", "bit", "kern", "user", "heap", "exe"); else printf("%-8s %3s %-8s %5s %5s %7s %s\n", "handle", "n", "base", "kern", "user", "heap", "exe"); } for (ProcessInfoMap::const_iterator i=pinfo.begin() ; i!=pinfo.end() ; ++i) { HANDLE h= (*i).first; const CEPROCESSENTRY &pe= (*i).second; const ProcessTimes *pt0= GetTime(tm0,h); const ProcessTimes *pt1= GetTime(tm1,h); double pmUser = -1; double pmKernel= -1; if (pt0 && pt1) { // NOTE: due to unknown reasons, sometimes the second tUser is smaller than the first tUser // i assume this is a kernel bug, since tUser should be the accumulated process time. // // -> this happens when threads are created/destroyed between // measurements. // pmUser= (((int)pt1->tUser-pt0->tUser)*100.0)/measurementtime; pmKernel= (((int)pt1->tKernel-pt0->tKernel)*100.0)/measurementtime; if (pmUser>100) printf("DEBUG: pt1usr=%08lx pt0usr=%08lx mt=%08lx\n", pt1->tUser, pt0->tUser, measurementtime); if (pmKernel>100) printf("DEBUG: pt1krn=%08lx pt0krn=%08lx mt=%08lx\n", pt1->tKernel, pt0->tKernel, measurementtime); pmUserTotal += pmUser; pmKernelTotal += pmKernel; if (bVerbose) printf("%08lx %3d %08lx %08lx %5.1f %5.1f %8d %ls%hs%ls\n", h, pt0->nThreads, pe.dwMemoryBase, 1<<((pe.dwMemoryBase>>25)-1), pmKernel, pmUser, pe.dwMemoryUsage, pe.szExeFile, pe.szCmdLine[0]?" ":"", pe.szCmdLine); else printf("%08lx %3d %08lx %5.1f %5.1f %8d %ls%hs%ls\n", h, pt0->nThreads, pe.dwMemoryBase, pmKernel, pmUser, pe.dwMemoryUsage, pe.szExeFile, pe.szCmdLine[0]?" ":"", pe.szCmdLine); dwTotalThreads += pt0->nThreads; } else if (pt0) { // absolute cpu time if (bVerbose) printf("%08lx %3d %08lx %08lx %10d %10d %8d %ls%hs%ls\n", h, pt0->nThreads, pe.dwMemoryBase, 1<<((pe.dwMemoryBase>>25)-1), pt0->tKernel, pt0->tUser, pe.dwMemoryUsage, pe.szExeFile, pe.szCmdLine[0]?" ":"", pe.szCmdLine); else printf("%08lx %3d %08lx %10d %10d %8d %ls%hs%ls\n", h, pt0->nThreads, pe.dwMemoryBase, pt0->tKernel, pt0->tUser, pe.dwMemoryUsage, pe.szExeFile, pe.szCmdLine[0]?" ":"", pe.szCmdLine); dwTotalThreads += pt0->nThreads; } else { // no timing info available if (bVerbose) printf("%08lx --- %08lx %08lx %8d %ls%hs%ls\n", h, pe.dwMemoryBase, 1<<((pe.dwMemoryBase>>25)-1), pmKernel, pmUser, pe.dwMemoryUsage, pe.szExeFile, pe.szCmdLine[0]?" ":"", pe.szCmdLine); else printf("%08lx --- %08lx %8d %ls%hs%ls\n", h, pe.dwMemoryBase, pmKernel, pmUser, pe.dwMemoryUsage, pe.szExeFile, pe.szCmdLine[0]?" ":"", pe.szCmdLine); } dwMemTotal += pe.dwMemoryUsage; } if (measurementtime==0) { if (bVerbose) printf(" %2d %3d ........ %10s %10s %8d total\n", pinfo.size(), dwTotalThreads, "", "", dwMemTotal); else printf(" %2d %3d ........ %10s %10s %8d total\n", pinfo.size(), dwTotalThreads, "", "", dwMemTotal); } else { if (bVerbose) printf(" %2d %3d ........ %5.1f %5.1f %8d total\n", pinfo.size(), dwTotalThreads, pmKernelTotal, pmUserTotal, dwMemTotal); else printf(" %2d %3d ........ %5.1f %5.1f %8d total\n", pinfo.size(), dwTotalThreads, pmKernelTotal, pmUserTotal, dwMemTotal); } } bool GetProcessInfo(bool bIncludeHeap, ProcessInfoMap &pinfo) { GetProcessListParams p; p.bIncludeHeapUsage= bIncludeHeap; DWORD outsize=0; GetProcessListResult *outbuf=NULL; HRESULT res= ItsutilsInvoke(L"ITGetProcessList", sizeof(GetProcessListParams), (BYTE*)&p, &outsize, (BYTE**)&outbuf); if (res || outbuf==NULL) { error(res, "ITGetProcessList"); return false; } if (outsizepe) || outsize < PTR_DIFF(outbuf, &outbuf->pe[outbuf->nEntries])) { debug("INTERNAL ERROR in itsutils.dll: expected %d bytes from ITGetProcessList, got %d\n", PTR_DIFF(outbuf, &outbuf->pe[outbuf->nEntries]), outsize); return false; } for (int i=0 ; inEntries ; i++) { memcpy(&pinfo[(HANDLE)outbuf->pe[i].dwProcessID], &outbuf->pe[i], sizeof(CEPROCESSENTRY)); } LocalFree(outbuf); return true; } // todo: make this more efficient. DWORD GetProcessSectionSlot(HANDLE hProc) { ProcessInfoMap pmap; if (GetProcessInfo(false, pmap)) if (pmap.find(hProc)!=pmap.end()) return pmap[hProc].dwMemoryBase; return 0; } bool ITGetModuleList(bool bDirectRead, ModuleEntryList &modlist) { DWORD outsize=0; GetModuleListResult *outbuf=NULL; GetModuleListParams p; p.bDirectRead= bDirectRead; HRESULT res= ItsutilsInvoke(L"ITGetModuleList", sizeof(GetModuleListParams), (BYTE*)&p, &outsize, (BYTE**)&outbuf); if (res || outbuf==NULL) { error(res, "ITGetModuleList"); return false; } if (outsizeme) || outsize < PTR_DIFF(outbuf, &outbuf->me[outbuf->nEntries])) { debug("INTERNAL ERROR in itsutils.dll: expected %d bytes from ITGetModuleList, got %d\n", PTR_DIFF(outbuf, &outbuf->me[outbuf->nEntries]), outsize); return false; } modlist.resize(outbuf->nEntries); memcpy(vectorptr(modlist), outbuf->me, sizeof(CEMODULEENTRY)*outbuf->nEntries); LocalFree(outbuf); return true; } bool GetProcessTimes(ProcessTimeMap &ptimes, DWORD& tQuery) { DWORD outsize=0; GetProcessUsageListResult *outbuf=NULL; HRESULT res= ItsutilsInvoke(L"ITGetProcessUsageList", 0, NULL, &outsize, (BYTE**)&outbuf); if (res || outbuf==NULL) { error(res, "ITGetProcessUsageList"); return false; } for (int i=0 ; ilist[i].hProc) ptimes[outbuf->list[i].hProc].tKernel= outbuf->list[i].tKernel; ptimes[outbuf->list[i].hProc].tUser= outbuf->list[i].tUser; ptimes[outbuf->list[i].hProc].nThreads= outbuf->list[i].nThreads; } tQuery= outbuf->tQuery; LocalFree(outbuf); return true; } void PrintThreadInfo(HANDLE hThread, DWORD tMeasure) { FILETIME tKernel0, tUser0, t0; FILETIME tKernel1, tUser1, t1; GetSystemTimeAsFileTime(&t0); if (!ITGetThreadTimes(hThread, NULL, NULL, &tKernel0, &tUser0)) return; if (tMeasure) { Sleep(tMeasure); GetSystemTimeAsFileTime(&t1); if (!ITGetThreadTimes(hThread, NULL, NULL, &tKernel1, &tUser1)) return; FILETIME tKernel, tUser, t; CalcFiletimeDiff(&tKernel0, &tKernel1, &tKernel); CalcFiletimeDiff(&tUser0, &tUser1, &tUser); CalcFiletimeDiff(&t0, &t1, &t); printf("kernel = %4d user = %4d\n", CalcPromile(&tKernel, &t), CalcPromile(&tUser, &t)); } else { printf("kernel = %10d user = %10d\n", tKernel0, tUser0); } } bool ITGetThreadTimes(HANDLE hThread, FILETIME *tCreate, FILETIME *tExit, FILETIME *tKernel, FILETIME *tUser) { DWORD outsize=0; GetThreadTimesResult *outbuf=NULL; GetThreadTimesParams p; p.hThread= hThread; HRESULT res= ItsutilsInvoke(L"ITGetThreadTimes", sizeof(p), (BYTE*)&p, &outsize, (BYTE**)&outbuf); if (res || outbuf==NULL) { error(res, "ITGetThreadTimes"); return false; } if (tCreate) *tCreate= outbuf->tCreate; if (tExit) *tExit= outbuf->tExit; if (tKernel) *tKernel= outbuf->tKernel; if (tUser) *tUser= outbuf->tUser; LocalFree(outbuf); return true; } BOOL CalcFiletimeDiff(const FILETIME *tFirst, const FILETIME *tLast, FILETIME *tDiff) { __int64 a, b, c; a= tFirst->dwHighDateTime; a<<=32; a |= tFirst->dwLowDateTime; b= tLast->dwHighDateTime; b<<=32; b |= tLast->dwLowDateTime; c= b-a; tDiff->dwHighDateTime= (DWORD) (c>>32); tDiff->dwLowDateTime= (DWORD) (c); return (BOOL)(c>>32); } DWORD CalcPromile(FILETIME *tA, FILETIME *tB) { __int64 a, b; a= tA->dwHighDateTime; a<<=32; a |= tA->dwLowDateTime; b= tB->dwHighDateTime; b<<=32; b |= tB->dwLowDateTime; if (b==0) return (DWORD)-1; a *= 1000; a /= b; return (DWORD)a; } bool ITGetThreadUsageList(bool resolve_modulenames, DWORD& tQuery, ThreadSummaryInfoMap& tinfo) { DWORD outsize=0; GetThreadUsageListResult *outbuf=NULL; GetThreadUsageListParams in; in.resolve_modulenames = resolve_modulenames; HRESULT res= ItsutilsInvoke(L"ITGetThreadUsageList", sizeof(in), (BYTE*)&in, &outsize, (BYTE**)&outbuf); if (res || outbuf==NULL) { error(res, "ITGetThreadUsageList"); return false; } tinfo.clear(); for (DWORD i=0 ; inThreads ; i++) tinfo[outbuf->list[i].hThread]= outbuf->list[i]; tQuery= outbuf->tQuery; LocalFree(outbuf); return true; } void PrintThreadEntry(const CEPROCESSENTRY* pe, const ThreadSummaryInfo& ti, const ThreadSummaryInfo* ti0, const ThreadSummaryInfo* ti1, DWORD measurementtime, bool bVerbose) { printf("%08lx %08lx %02x %02x", ti.hThread, ti.dwStartAddr, ti.baseprio, ti.curprio); if (bVerbose) printf(" %08lx %08lx %08lx", ti.dwCurPC, ti.dwCurLR, ti.dwCurSP); if (measurementtime==0 && ti0) { // absolute time printf(" %10d %10d", ti0->tKernel, ti0->tUser); } else if (ti1==NULL && ti0==NULL) printf(" "); else if (ti1==NULL) printf(" terminated "); else if (ti0==NULL) printf(" created "); else if (measurementtime) { double pmUser= (ti1->tUser-ti0->tUser)*100.0/measurementtime; double pmKernel= (ti1->tKernel-ti0->tKernel)*100.0/measurementtime; printf(" %5.1f %5.1f", pmKernel, pmUser); } else { printf("ERROR"); } if (ti.szModname[0]) printf(" %-12ls", ti.szModname); else printf(" "); printf(" %08lx", ti.hProc); if (pe) printf(": %ls%hs%ls\n", pe->szExeFile, pe->szCmdLine[0]?" ":"", pe->szCmdLine); else printf(": ... unknown process\n"); } const CEPROCESSENTRY* getmapentry(const ProcessInfoMap& map, HANDLE key) { ProcessInfoMap::const_iterator i= map.find(key); if (i==map.end()) return NULL; return &(*i).second; } void PrintThreadList(const ProcessInfoMap &pinfo, const ThreadSummaryInfoMap& tsim0, const ThreadSummaryInfoMap& tsim1, DWORD measurementtime, bool bVerbose, HANDLE hProcFilter) { for (ThreadSummaryInfoMap::const_iterator i=tsim0.begin() ; i!=tsim0.end() ; ++i) { const ThreadSummaryInfo& ti0= (*i).second; if (hProcFilter!=INVALID_HANDLE_VALUE && ti0.hProc!=hProcFilter) continue; const CEPROCESSENTRY *pe= getmapentry(pinfo,ti0.hProc); ThreadSummaryInfoMap::const_iterator i1= tsim1.find((*i).first); if (tsim1.empty()) { PrintThreadEntry(pe, ti0, &ti0, NULL, 0, bVerbose); } else if (i1==tsim1.end()) { PrintThreadEntry(pe, ti0, &ti0, NULL, measurementtime, bVerbose); } else { const ThreadSummaryInfo& ti1= (*i1).second; PrintThreadEntry(pe, ti1, &ti0, &ti1, measurementtime, bVerbose); } } for (ThreadSummaryInfoMap::const_iterator i=tsim1.begin() ; i!=tsim1.end() ; ++i) { const ThreadSummaryInfo& ti1= (*i).second; if (hProcFilter!=INVALID_HANDLE_VALUE && ti1.hProc!=hProcFilter) continue; ThreadSummaryInfoMap::const_iterator i0= tsim0.find((*i).first); const CEPROCESSENTRY *pe= getmapentry(pinfo,ti1.hProc); if (i0==tsim0.end()) PrintThreadEntry(pe, ti1, NULL, &ti1, measurementtime, bVerbose); } } void PrintModuleList(bool bVerbose, const ModuleEntryList& modlist, DWORD dwProcMask) { if (bVerbose) printf("membase vbase dbase usage name\n"); else printf("membase name\n"); for (size_t i=0 ; i