Thread: Windows Version of bash's 'time' builtin

  1. #1
    Registered User
    Join Date
    Sep 2001
    Posts
    752

    Windows Version of bash's 'time' builtin

    I'm looking for a windows program that will output timing statistics for executing a command.

    Similar to the bash 'time' builtin.

    Code:
    jgalloway@boc-jgalloway ~
    $ time find training/
    
    ... lists a buncha files
    
    real    0m0.765s
    user    0m0.046s
    sys     0m0.031s
    
    jgalloway@boc-jgalloway ~
    $
    Callou collei we'll code the way
    Of prime numbers and pings!

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    It doesn't have to be a cygwin or bash command to be able to time it.

    $ time notepad

    real 0m4.086s
    user 0m0.020s
    sys 0m0.560s
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Registered User
    Join Date
    Sep 2001
    Posts
    752
    I need to be in a Windows environment. It can't be running under bash (I'm timing a complicated build process which makes assumptions about the OS and environment).

    In theory... I could time a command script that creates a windows shell and performs all the setup needed for the build... but this build is complicated enough, I don't need more variables.
    Callou collei we'll code the way
    Of prime numbers and pings!

  4. #4
    Registered User Tonto's Avatar
    Join Date
    Jun 2005
    Location
    New York
    Posts
    1,465
    Cygwin?

  5. #5
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    Process Viewer?
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  6. #6
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    http://faq.cprogramming.com/cgi-bin/...&id=1043284392
    Use reateProcess() to create the process.
    While you're waiting for it to finish, use GetProcessTimes() to see how it's doing.

    You'll have to look on MSDN to find out about that.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  7. #7
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    Quote Originally Posted by Salem
    http://faq.cprogramming.com/cgi-bin/...&id=1043284392
    Use reateProcess() to create the process.
    While you're waiting for it to finish, use GetProcessTimes() to see how it's doing.

    You'll have to look on MSDN to find out about that.
    Great minds... I tried that approach yesterday. The only problem was that CreateProcess returns the handle to the command interpreter rather than the process executing the target command, giving incorrect results for user and kernel times (if you didn't need the command interpreter, you could just launch the target process directly). Today, I used a job object which seems to work well:
    Code:
    #define _WIN32_WINNT 0x0500
    #define UNICODE
    #define _UNICODE
    #include <windows.h>
    #include <stdio.h>
    #include <tchar.h>
    #include <strsafe.h>
    
    #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
    
    typedef union
    {
    	LARGE_INTEGER ul;
    	FILETIME       ft;
    } TIME;
    
    
    #define FT_SECONDS (10000000)             /* Number of filetime units in a second. */
    #define FT_MINUTES (60 * FT_SECONDS)      /* Number of filetime units in a minute. */
    #define FT_MILLI   (FT_SECONDS / 1000)    /* Number of filetime units in a millisecond. */
    
    
    /* Get a TCHAR version of WinMain's lpCmdLine. That is the command
     * line excluding the program name. szCmdArgs should be 4096 TCHARs.
     */
    void GetCommandArgs(LPTSTR szCmdArgs)
    {
    	LPTSTR szEndOfProgramName;
    	LPTSTR szCompleteCmdLine = GetCommandLine();
    
    	if (szCompleteCmdLine[0] == TEXT('\"'))
    		szEndOfProgramName = _tcschr(&szCompleteCmdLine[1], TEXT('\"'));
    	else
    		szEndOfProgramName = _tcschr(szCompleteCmdLine, TEXT(' '));
    
    	_tcsncpy(szCmdArgs, (szEndOfProgramName ? &szEndOfProgramName[1] : TEXT("")), 4090);
    
    	// Terminate as _tcsncpy will not always do so...
    	szCmdArgs[4090] = TEXT('\0');
    }
    
    
    /* Run a command with the default shell and associate with a job object.
     */
    HANDLE RunCommand(LPCTSTR szCommand, HANDLE hJob)
    {
    	TCHAR               szComSpec[MAX_PATH];
    	TCHAR               szCmd[2048];
    	STARTUPINFO         si = { sizeof(si) };
    	PROCESS_INFORMATION pi = { 0 };
    
    	if ( SUCCEEDED(StringCchPrintf(szCmd, ARRAY_SIZE(szCmd), TEXT("/c %s"), szCommand)) &&
    	     GetEnvironmentVariable(TEXT("COMSPEC"), szComSpec, ARRAY_SIZE(szComSpec))      && 
    	     CreateProcess(szComSpec, szCmd, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi) )
    	{
    		AssignProcessToJobObject(hJob, pi.hProcess);
    		ResumeThread(pi.hThread);
    		CloseHandle(pi.hThread);
    		WaitForSingleObject(pi.hProcess, INFINITE);
    
    		return pi.hProcess;
    	}
    
    	return NULL;
    }
    
    
    /* main
     */
    int main(void)
    {
    	HANDLE hProcess;
    	HANDLE hJob;
    	TIME   tmCreate, tmExit, tmKernel, tmUser, tmElapsed, tmDummy[2];
    	TCHAR  szCommand[4096];
    	JOBOBJECT_BASIC_ACCOUNTING_INFORMATION jbai;
    
    	GetCommandArgs(szCommand);
    
    	hJob = CreateJobObject(NULL, NULL);
    	hProcess = RunCommand(szCommand, hJob);
    
    	/* Use GetProcessTimes to get the real time. */
    	GetProcessTimes(hProcess, &tmCreate.ft, &tmExit.ft, &tmDummy[0].ft, &tmDummy[1].ft);
    	tmElapsed.ul.QuadPart = tmExit.ul.QuadPart - tmCreate.ul.QuadPart;
    
    	/* Query the job object to get user and kernel time for all processes in the job object. */
    	QueryInformationJobObject(hJob, JobObjectBasicAccountingInformation, &jbai, sizeof(jbai), NULL);
    	tmUser.ul = jbai.TotalUserTime;
    	tmKernel.ul = jbai.TotalKernelTime;
    
    	CloseHandle(hProcess);
    	CloseHandle(hJob);
    
    	printf("real    %lum%lu.%03lus\n", 
    	       (ULONG) ((tmElapsed.ul.QuadPart / FT_MINUTES)),
    	       (ULONG) ((tmElapsed.ul.QuadPart % FT_MINUTES) / FT_SECONDS),
    	       (ULONG) ((tmElapsed.ul.QuadPart % FT_SECONDS) / FT_MILLI));
    
    	printf("user    %lum%lu.%03lus\n", 
    	       (ULONG) ((tmUser.ul.QuadPart / FT_MINUTES)),
    	       (ULONG) ((tmUser.ul.QuadPart % FT_MINUTES) / FT_SECONDS),
    	       (ULONG) ((tmUser.ul.QuadPart % FT_SECONDS) / FT_MILLI));
    
    	printf("sys     %lum%lu.%03lus\n", 
    	       (ULONG) ((tmKernel.ul.QuadPart / FT_MINUTES)),
    	       (ULONG) ((tmKernel.ul.QuadPart % FT_MINUTES) / FT_SECONDS),
    	       (ULONG) ((tmKernel.ul.QuadPart % FT_SECONDS) / FT_MILLI));
    
    	return 0;
    }
    And, for the record, here is the original:
    Code:
    #include <windows.h>
    #include <stdio.h>
    #include <tchar.h>
    #include <strsafe.h>
    
    #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
    
    typedef union
    {
    	ULARGE_INTEGER ul;
    	FILETIME       ft;
    } TIME;
    
    
    #define FT_SECONDS (10000000)             /* Number of filetime units in a second. */
    #define FT_MINUTES (60 * FT_SECONDS)      /* Number of filetime units in a minute. */
    #define FT_MILLI   (FT_SECONDS / 1000)    /* Number of filetime units in a millisecond. */
    
    
    /* Get a TCHAR version of WinMain's lpCmdLine. That is the command
     * line excluding the program name. szCmdArgs should be 4096 TCHARs.
     */
    void GetCommandArgs(LPTSTR szCmdArgs)
    {
    	LPTSTR szEndOfProgramName;
    	LPTSTR szCompleteCmdLine = GetCommandLine();
    
    	if (szCompleteCmdLine[0] == TEXT('\"'))
    		szEndOfProgramName = _tcschr(&szCompleteCmdLine[1], TEXT('\"'));
    	else
    		szEndOfProgramName = _tcschr(szCompleteCmdLine, TEXT(' '));
    
    	_tcsncpy(szCmdArgs, (szEndOfProgramName ? &szEndOfProgramName[1] : TEXT("")), 4090);
    
    	// Terminate as _tcsncpy will not always do so...
    	szCmdArgs[4090] = TEXT('\0');
    }
    
    
    /* Run a command with the default shell.
     */
    HANDLE RunCommand(LPCTSTR szCommand)
    {
    	TCHAR               szComSpec[MAX_PATH];
    	TCHAR               szCmd[2048];
    	STARTUPINFO         si = { sizeof(si) };
    	PROCESS_INFORMATION pi = { 0 };
    
    	if ( SUCCEEDED(StringCchPrintf(szCmd, ARRAY_SIZE(szCmd), TEXT("/c %s"), szCommand)) &&
    	     GetEnvironmentVariable(TEXT("COMSPEC"), szComSpec, ARRAY_SIZE(szComSpec))      && 
    	     CreateProcess(szComSpec, szCmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi) )
    	{
    		CloseHandle(pi.hThread);
    		WaitForSingleObject(pi.hProcess, INFINITE);
    
    		return pi.hProcess;
    	}
    
    	return NULL;
    }
    
    
    /* main
     */
    int main(void)
    {
    	HANDLE hProcess;
    	TIME   tmCreate, tmExit, tmKernel, tmUser, tmElapsed;
    	TCHAR  szCommand[4096];
    
    	GetCommandArgs(szCommand);
    
    	hProcess = RunCommand(szCommand);
    
    	GetProcessTimes(hProcess, &tmCreate.ft, &tmExit.ft, &tmKernel.ft, &tmUser.ft);
    	CloseHandle(hProcess);
    	tmElapsed.ul.QuadPart = tmExit.ul.QuadPart - tmCreate.ul.QuadPart;
    
    	printf("real    %lum%lu.%03lus\n", 
    	       (ULONG) ((tmElapsed.ul.QuadPart / FT_MINUTES)),
    	       (ULONG) ((tmElapsed.ul.QuadPart % FT_MINUTES) / FT_SECONDS),
    	       (ULONG) ((tmElapsed.ul.QuadPart % FT_SECONDS) / FT_MILLI));
    
    	printf("user    %lum%lu.%03lus\n", 
    	       (ULONG) ((tmUser.ul.QuadPart / FT_MINUTES)),
    	       (ULONG) ((tmUser.ul.QuadPart % FT_MINUTES) / FT_SECONDS),
    	       (ULONG) ((tmUser.ul.QuadPart % FT_SECONDS) / FT_MILLI));
    
    	printf("sys     %lum%lu.%03lus\n", 
    	       (ULONG) ((tmKernel.ul.QuadPart / FT_MINUTES)),
    	       (ULONG) ((tmKernel.ul.QuadPart % FT_MINUTES) / FT_SECONDS),
    	       (ULONG) ((tmKernel.ul.QuadPart % FT_SECONDS) / FT_MILLI));
    
    	return 0;
    }

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. How to get and set windows time and date
    By huwan in forum Windows Programming
    Replies: 18
    Last Post: 05-13-2008, 10:33 AM
  2. Codec Bitrates?
    By gvector1 in forum C# Programming
    Replies: 2
    Last Post: 06-16-2003, 08:39 AM
  3. Windows Version
    By Yoshi in forum C++ Programming
    Replies: 1
    Last Post: 01-09-2002, 11:34 AM
  4. directory and windows version?
    By davebaggott in forum Windows Programming
    Replies: 9
    Last Post: 10-13-2001, 02:31 PM