/* (C) 2003-2007 Willem Jan Hengeveld <itsme@xs4all.nl>
 * Web: http://www.xs4all.nl/~itsme/
 *      http://wiki.xda-developers.com/
 *
 * $Id$
 *
 * 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
 *
 * todo: add sorting options:
 *   -Op  = sort by cpu usage
 *   -Oh  = sort by heap usage
 *   -On  = sort by processname
 *   -Ov  = sort by slotnr
 *
 *
 * todo: add option to continue measuring
 *
 */
#include <util/wintypes.h>
#include <stdio.h>
#include <string.h>
#include <util/rapitypes.h>

#include <map>

#define MEASUREMENT_MSEC 200

#include "itsutils.h"
#include "dllversion.h"

#include "debug.h"
#include "ptrutils.h"
#include "args.h"
#include "util/HiresTimer.h"
#include "util/timeconvert.h"

class ProcessTimes {
public:
    DWORD nThreads;
    DWORD tKernel;
    DWORD tUser;
};
typedef std::map<HANDLE,ProcessTimes> ProcessTimeMap;

typedef std::map<HANDLE,CEPROCESSENTRY> ProcessInfoMap;
typedef std::map<HANDLE,ThreadSummaryInfo> ThreadSummaryInfoMap;

void PrintProcessList(const ProcessInfoMap &pinfo, const ProcessTimeMap &ptm0, const ProcessTimeMap &ptm1, DWORD measurementtime, bool bVerbose, bool bContinuous, HANDLE hProcFilter);
bool GetProcessInfo(bool bIncludeHeap, ProcessInfoMap &pinfo);
bool GetProcessTimes(ProcessTimeMap &ptimes, DWORD &tQuery);

bool ITGetThreadUsageList(bool resolve_modulenames, DWORD& tQuery, ThreadSummaryInfoMap& tinfo);
HANDLE ITGetProcessHandle(const std::string& 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);
int64_t CalcFiletimeDiff(const FILETIME *tFirst, const FILETIME *tLast);
DWORD CalcPromile(int64_t a, int64_t b);
DWORD GetProcessSectionSlot(HANDLE hProc);

typedef std::vector<CEMODULEENTRY> ModuleEntryList;
bool ITGetModuleList(bool bDirectRead, ModuleEntryList &pinfo);
void PrintModuleList(bool bVerbose, const ModuleEntryList& modlist, DWORD dwProcMask);

void write_timestamp()
{
    debugt("\n");
}
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("   -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("   -c        : continuously update\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 bContinuous= false;
    bool bListAllThreads= false;
    bool bIncludeHeap= false;
    bool bListModules= false;

    int argsfound=0;
    for (int i=1 ; i<argc ; i++)
    {
        if (argv[i][0]=='-') switch(argv[i][1])
        {
            case 't': bListAllThreads= true; break;
            case 'p': HANDLEULOPTION(hProcess, HANDLE); break;
            case 'n': HANDLESTROPTION(szProcessName); break;
            case 's': HANDLEULOPTION(tMeasure, DWORD); break;
            case 'c': bContinuous= true; break;
//            case 'h': bIncludeHeap= true; break;
            case 'm': bListModules= true; break;
            case 'v': bVerbose= true; break;
            default:
                usage();
                return 1;
        }
        else 
            argsfound++;
    }
    if (argsfound)
    {
        usage();
        return 1;
    }

    CheckITSDll();
    if (bVerbose && !bContinuous)
        PrintContext();
    if (szProcessName)
        hProcess= ITGetProcessHandle(szProcessName);
    DWORD dwProcMask= 0xFFFFFFFF;

    if (hProcess!=INVALID_HANDLE_VALUE) {
        DWORD dwSection= GetProcessSectionSlot(hProcess);
        if (dwSection==0xc2000000) {
            dwProcMask = 1;
        }
        else if (dwSection) {
            dwProcMask = 1<<((dwSection>>25)-1);
        }
    }


    if (bListModules) {
        // ! abusing -h = bIncludeHeap  as alternate mode for modlist
        ModuleEntryList modlist;
        if (!ITGetModuleList(dwProcMask==0xFFFFFFFF ? bVerbose : true, modlist))
            return 1;
        PrintModuleList(bVerbose, 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) {
            do {
                HiresTimer::usleep(1000LL*tMeasure);

                if (bContinuous)
                    write_timestamp();

                if (!ITGetThreadUsageList(true, tQuery1, tsim1))
                    return 1;
                PrintThreadList(pinfo, tsim0, tsim1, tQuery1-tQuery0, bVerbose, hProcess);
            } while (bContinuous);
        }
        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) {
            tm1.swap(tm0);
            do {
                HiresTimer::usleep(1000LL*tMeasure);

                if (bContinuous) {
                    write_timestamp();
                    if (bVerbose)
                        PrintContext();
                }

                tm0.swap(tm1);
                if (!GetProcessTimes(tm1, tQuery1))
                    return 1;
                PrintProcessList(pinfo, tm0, tm1, tQuery1-tQuery0, bVerbose, bContinuous, hProcess);

                if (bContinuous) {
                    // update pinfo too
                    if (!GetProcessInfo(bIncludeHeap, pinfo))
                        return 1;

                }
            } while (bContinuous);
        }
        else {
            PrintProcessList(pinfo, tm0, tm1, 0, bVerbose, bContinuous, hProcess);
        }

    }

    StopItsutils();

    return 0;
}

void PrintContext()
{
    DWORD outsize=0;
    GetContextResult *outbuf=NULL;

    HRESULT res= ItsutilsInvoke("ITGetContext",
            0, NULL, &outsize, (BYTE**)&outbuf);

    //printf("result=%08lx  buf=%08lx size=%08lx\n", res, outbuf, outsize);
    if (res==0 && outbuf!=NULL)
    {
        verify_size(outbuf, outbuf->wszCmdLine+stringlength(outbuf->wszCmdLine)+1, outsize, "ITGetContext");
        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);
        RapiFree(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;
}
double percentage(DWORD t0, DWORD t1, DWORD tmeasure)
{
    if (t0>t1)
        return (t0-t1)*-100.0/tmeasure;
    else
        return (t1-t0)*100.0/tmeasure;
}


void PrintProcessList(const ProcessInfoMap &pinfo, const ProcessTimeMap &tm0, const ProcessTimeMap &tm1, DWORD measurementtime, bool bVerbose, bool bContinuous, HANDLE hProcFilter)
{
    double pmUserTotal=0;
    double pmKernelTotal=0;
    DWORD dwMemTotal=0;
    DWORD dwTotalThreads=0;

    if (hProcFilter==INVALID_HANDLE_VALUE) {
        if (measurementtime==0) {
            // '-s0' option
            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 (hProcFilter!=INVALID_HANDLE_VALUE && h!=hProcFilter)
            continue;
        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= percentage(pt0->tUser, pt1->tUser, measurementtime);
            if (pmUser>0)    pmUserTotal += pmUser;

            pmKernel= percentage(pt0->tKernel, pt1->tKernel, measurementtime);
            if (pmKernel>0) pmKernelTotal += pmKernel;

            if (bVerbose)
                printf("%08lx %3d %08lx %08lx %5.1f %5.1f %8d %ls%hs%ls\n", h, pt1->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, pt1->nThreads, pe.dwMemoryBase, pmKernel, pmUser, pe.dwMemoryUsage, pe.szExeFile, pe.szCmdLine[0]?" ":"", pe.szCmdLine);
            dwTotalThreads += pt1->nThreads;
        }
        else if (bContinuous) {
            dwMemTotal -= pe.dwMemoryUsage;

            // process termninated
//          if (bVerbose)
//              printf("%08lx --- -------- -------- ----- -----          %ls%hs%ls\n", h, pe.szExeFile, pe.szCmdLine[0]?" ":"", pe.szCmdLine);
//          else
//              printf("%08lx --- -------- ----- -----          %ls%hs%ls\n", h, pe.szExeFile, pe.szCmdLine[0]?" ":"", pe.szCmdLine);

        }
        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), pe.dwMemoryUsage, pe.szExeFile, pe.szCmdLine[0]?" ":"", pe.szCmdLine);
            else
                printf("%08lx --- %08lx             %8d %ls%hs%ls\n", h, pe.dwMemoryBase, pe.dwMemoryUsage, pe.szExeFile, pe.szCmdLine[0]?" ":"", pe.szCmdLine);
        }
        dwMemTotal += pe.dwMemoryUsage;
    }

    if (hProcFilter==INVALID_HANDLE_VALUE) {
        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("ITGetProcessList",
            sizeof(GetProcessListParams), (BYTE*)&p, &outsize, (BYTE**)&outbuf);

    if (res || outbuf==NULL)
    {
        error(res, "ITGetProcessList");
        return false;
    }
    verify_size(outbuf, outbuf->pe+outbuf->nEntries, outsize, "ITGetProcessList");

    for (int i=0 ; i<outbuf->nEntries ; i++)
    {
        memcpy(&pinfo[(HANDLE)outbuf->pe[i].dwProcessID], &outbuf->pe[i], sizeof(CEPROCESSENTRY));
    }

    RapiFree(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("ITGetModuleList",
            sizeof(GetModuleListParams), (BYTE*)&p, &outsize, (BYTE**)&outbuf);

    if (res || outbuf==NULL)
    {
        error(res, "ITGetModuleList");
        return false;
    }
    verify_size(outbuf, outbuf->me+outbuf->nEntries, outsize, "ITGetModuleList");

    modlist.resize(outbuf->nEntries);
    memcpy(vectorptr(modlist), outbuf->me, sizeof(CEMODULEENTRY)*outbuf->nEntries);

    RapiFree(outbuf);
    
    return true;
}


bool GetProcessTimes(ProcessTimeMap &ptimes, DWORD& tQuery)
{
    DWORD outsize=0;
    GetProcessUsageListResult *outbuf=NULL;

    HRESULT res= ItsutilsInvoke("ITGetProcessUsageList",
            0, NULL, &outsize, (BYTE**)&outbuf);

    if (res || outbuf==NULL)
    {
        error(res, "ITGetProcessUsageList");
        return false;
    }
    verify_size(outbuf, outbuf->list+outbuf->nEntries, outsize, "ITGetProcessUsageList");


    ptimes.clear();

    for (int i=0 ; i<outbuf->nEntries ; i++)
    {
        if (outbuf->list[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;

    RapiFree(outbuf);
    
    return true;
}

void PrintThreadInfo(HANDLE hThread, DWORD tMeasure)
{
    FILETIME tKernel0, tUser0;
    FILETIME tKernel1, tUser1;


    HiresTimer t0;
    if (!ITGetThreadTimes(hThread, NULL, NULL, &tKernel0, &tUser0))
        return;
    if (tMeasure) {
        HiresTimer::usleep(1000LL*tMeasure);
        uint32_t t= t0.elapsed();
        if (!ITGetThreadTimes(hThread, NULL, NULL, &tKernel1, &tUser1))
            return;

        int64_t tKernel= CalcFiletimeDiff(&tKernel0, &tKernel1);
        int64_t tUser= CalcFiletimeDiff(&tUser0, &tUser1);

        printf("kernel = %4d  user = %4d\n", CalcPromile(tKernel, t), CalcPromile(tUser, t));
    }
    else {
        printf("kernel = %10lld user = %10lld\n", FiletimeToInt64(&tKernel0), FiletimeToInt64(&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("ITGetThreadTimes",
            sizeof(p), (BYTE*)&p, &outsize, (BYTE**)&outbuf);

    if (res || outbuf==NULL)
    {
        error(res, "ITGetThreadTimes");
        return false;
    }
    verify_size(outbuf, outbuf+1, outsize, "ITGetThreadTimes");
    if (tCreate) *tCreate= outbuf->tCreate;
    if (tExit) *tExit= outbuf->tExit;
    if (tKernel) *tKernel= outbuf->tKernel;
    if (tUser) *tUser= outbuf->tUser;

    RapiFree(outbuf);

    return true;
}


int64_t CalcFiletimeDiff(const FILETIME *tFirst, const FILETIME *tLast)
{
    return FiletimeToInt64(tLast)- FiletimeToInt64(tFirst);
}

DWORD CalcPromile(int64_t a, int64_t b)
{
    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("ITGetThreadUsageList",
            sizeof(in), (BYTE*)&in, &outsize, (BYTE**)&outbuf);

    if (res || outbuf==NULL)
    {
        error(res, "ITGetThreadUsageList");
        return false;
    }
    verify_size(outbuf, outbuf->list+outbuf->nThreads, outsize, "ITGetThreadUsageList");

    tinfo.clear();
    for (DWORD i=0 ; i<outbuf->nThreads ; i++)
        tinfo[outbuf->list[i].hThread]= outbuf->list[i];

    tQuery= outbuf->tQuery;

    RapiFree(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= percentage(ti0->tUser,ti1->tUser, measurementtime);
        double pmKernel= percentage(ti0->tKernel,ti1->tKernel, 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<modlist.size() ; i++)
    {
        if (dwProcMask & modlist[i].dwUsage) {
            if (bVerbose)
                printf("%08lx: %08lx %08lx %08lx %08lx %ls\n", modlist[i].hLib, modlist[i].dwMemoryBase, modlist[i].dwVBase, modlist[i].dwDBase, modlist[i].dwUsage, modlist[i].szModuleName);
            else
                printf("%08lx: %08lx %ls\n", modlist[i].hLib, modlist[i].dwMemoryBase, modlist[i].szModuleName);
        }
    }
}

HANDLE ITGetProcessHandle(const std::string& processName)
{
    std::Wstring wprocname= ToWString(processName);
    DWORD insize= (wprocname.size()+1)*sizeof(WCHAR);
    WCHAR *inbuf= (WCHAR*)RapiAlloc(insize);

    std::copy(wprocname.begin(), wprocname.end(), inbuf);
    inbuf[wprocname.size()]= 0;

    DWORD outsize=0;
    HANDLE *outbuf=NULL;

    HRESULT res= ItsutilsInvoke("ITGetProcessHandle",
            insize, (BYTE*)inbuf,
            &outsize, (BYTE**)&outbuf);
    if (res || outbuf==NULL) 
    {
        error(res, "ITGetProcessHandle");
        return INVALID_HANDLE_VALUE;
    }
    verify_size(outbuf, outbuf+1, outsize, "ITGetProcessHandle");

    HANDLE hproc= *outbuf;

    RapiFree(outbuf);

    return hproc;
}
