News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

How long program has run

Started by Magnum, June 05, 2010, 01:40:56 AM

Previous topic - Next topic

Magnum

I am working on a program to tell me how long my browser has been running.
This stores the start and stop times and am trying to figure out how to subtract and print the result to a log file.

I know that my time mask will have to be changed.



.DATA

szCommandLine  BYTE   "C:\Program Files\Mozilla Firefox\firefox.exe",0
start_Time db "00:00:00",0 ; time mask
end_Time   db "00:00:00",0 ; time mask

.code

start:

Begin PROC
   
LOCAL SystemInfo:STARTUPINFO
LOCAL ProcessInfo:PROCESS_INFORMATION

  MOV SystemInfo.cb, SIZEOF SystemInfo
  INVOKE GetStartupInfo, ADDR SystemInfo
  OR SystemInfo.dwFlags,STARTF_USESHOWWINDOW
  MOV SystemInfo.wShowWindow, SW_HIDE

; start timer
invoke GetTimeFormat,0,TIME_FORCE24HOURFORMAT,0,0,addr start_Time,9 ; get current time
int 3
INVOKE CreateProcess,NULL,ADDR szCommandLine, NULL, NULL, FALSE, \
       NORMAL_PRIORITY_CLASS, NULL, NULL, ADDR SystemInfo, ADDR ProcessInfo


     INVOKE CloseHandle,ProcessInfo.hProcess
     INVOKE CloseHandle,ProcessInfo.hThread 
 
; end timer
invoke GetTimeFormat,0,TIME_FORCE24HOURFORMAT,0,0,addr end_Time,9

invoke  ExitProcess,NULL

Begin   endp

   
end     start
Have a great day,
                         Andy

qWord

FPU in a trice: SmplMath
It's that simple!

Magnum

Quote from: qWord on June 05, 2010, 01:50:02 AM
Take a look at GetProcessTimes

I modified MichaelW's code, but it isn't working.




include \masm32\include\masm32rt.inc


printf MACRO format:REQ, args:VARARG
      IFNB <args>
        invoke crt_printf, cfm$(format), args
      ELSE
        invoke crt_printf, cfm$(format)
      ENDIF
    ENDM

.DATA

szCommandLine  BYTE   "C:\Program Files\Mozilla Firefox\firefox.exe",0
creationTime   dq 0
sysTime        dq 0
runTime        REAL8 ?
hProcess       dd 0

.code

start:

Begin PROC
   
LOCAL SystemInfo:STARTUPINFO
LOCAL ProcessInfo:PROCESS_INFORMATION

  MOV SystemInfo.cb, SIZEOF SystemInfo
  INVOKE GetStartupInfo, ADDR SystemInfo
  OR SystemInfo.dwFlags,STARTF_USESHOWWINDOW
  MOV SystemInfo.wShowWindow, SW_HIDE

; start firefox
INVOKE CreateProcess,NULL,ADDR szCommandLine, NULL, NULL, FALSE, \
       NORMAL_PRIORITY_CLASS, NULL, NULL, ADDR SystemInfo, ADDR ProcessInfo

invoke GetCurrentProcessId

    invoke OpenProcess, PROCESS_QUERY_INFORMATION, FALSE, eax
    mov hProcess, eax

    invoke GetProcessTimes, hProcess, ADDR creationTime, ADDR sysTime,
                            ADDR sysTime, ADDR sysTime

    invoke GetSystemTimeAsFileTime, ADDR sysTime

    fild sysTime
    fild creationTime
    fsub
    fld8 100.0e-9
    fmul
    fstp runTime

    printf "%.1fs\n\n", runTime

    inkey "Press any key to exit..."
    exit

Begin   endp
   
end     start

Have a great day,
                         Andy

qWord

what about this one:
include \masm32\include\masm32rt.inc

.data
szCommandLine  BYTE   "C:\Windows\notepad.exe",0
.code
Begin PROC   
LOCAL sui:STARTUPINFO
LOCAL pi:PROCESS_INFORMATION
LOCAL creationTime:QWORD
LOCAL exitTime:QWORD
LOCAL sysTime:QWORD
LOCAL runTime:REAL8

    ; start notepad ;-)
    INVOKE CreateProcess,NULL,ADDR szCommandLine, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, ADDR sui, ADDR pi
    invoke WaitForSingleObject,pi.hProcess,INFINITE
    invoke GetProcessTimes, pi.hProcess, ADDR creationTime, ADDR exitTime,ADDR sysTime, ADDR sysTime
    invoke CloseHandle,pi.hProcess

    fild exitTime
    fild creationTime
    fsubp st(1),st
    fld8 100.0e-9
    fmulp
    fstp runTime

    print real8$(runTime),10,13
    inkey "Press any key to exit..."
    exit

Begin   endp
end     Begin
FPU in a trice: SmplMath
It's that simple!

Magnum

Thanks QWord, but the code does not work because you close it too soon.

I want to take the time when the program starts and when it is closed and then calculate the time difference.

Have a great day,
                         Andy

qWord

Quote from: Magnum on June 05, 2010, 03:47:55 PM
Thanks QWord, but the code does not work because you close it too soon.
surly not: WaitForSingleObject,pi.hProcess,INFINITE

EDIT: maybe CreateProcess() fails - check the return value
FPU in a trice: SmplMath
It's that simple!

Tedd

No snowflake in an avalanche feels responsible.

Magnum

Thanks Tedd.

This code runs fine.
I now want to format it to show minutes and seconds.

I think this will need to be changed.

result      db "real:%lums user:%lums kern:%lums",CR,LF,0

I want to learn about the % stuff below, but could not find any reference that explains it.

Andy





start:
    invoke GetStdHandle, STD_OUTPUT_HANDLE
    mov hStdOut,eax
    invoke GetStdHandle, STD_ERROR_HANDLE
    mov hStdErr,eax

    push esi
    invoke GetCommandLine
    mov esi,eax
    invoke skip_arg, eax    ;first arg is this program's name, we want everything after it
    ;;what to do with pipes - should it redirect from timit, the invoked process, or both??
    .IF (eax != -1)
        push ebx
        add esi,eax

        mov ebx,OFFSET stInfo
        assume ebx:ptr STARTUPINFO
        xor eax,eax
        mov [ebx].cb,SIZEOF stInfo
        mov [ebx].lpReserved,eax
        mov [ebx].lpDesktop,eax
        mov [ebx].lpTitle,eax
        mov [ebx].dwFlags,eax
        mov DWORD PTR [ebx].wShowWindow,eax   ;also zeroes 'cbReserved2'
        mov [ebx].lpReserved2,eax
        assume edx:nothing

        invoke CreateProcess, eax,esi,eax,eax,TRUE,eax,eax,eax,ebx,ADDR procInfo
        .IF (eax)

            mov ebx,[procInfo.hProcess]
            invoke WaitForSingleObject, ebx,-1
            invoke GetProcessTimes, ebx,ADDR timeSt,ADDR timeEnd,ADDR timeKernel,ADDR timeUser

            ;** these divsions could overflow -- if result is >= 2^32; but 2^32ms is actually 49.71 days) **

            mov ecx,10000

            ;kernel time (in milliseconds)
            mov eax,[timeKernel.dwLowDateTime]
            mov edx,[timeKernel.dwHighDateTime]
            ;convert to milliseconds
            add eax,5000    ;(so division rounds up)
            adc edx,0
            div ecx
            push eax

            ;user time
            mov eax,[timeUser.dwLowDateTime]
            mov edx,[timeUser.dwHighDateTime]
            add eax,5000
            adc edx,0
            div ecx
            push eax

            ;execution time = (timeEnd - timeSt)
            mov eax,[timeEnd.dwLowDateTime]
            mov edx,[timeEnd.dwHighDateTime]
            sub eax,[timeSt.dwLowDateTime]
            sbb edx,[timeSt.dwHighDateTime]
            mov ecx,10000
            add eax,5000
            adc edx,0
            div ecx
            push eax

            push OFFSET result
            push OFFSET buff
            call wsprintf
            add esp,(4*5)

invoke CreateFile,CTEXT("result.txt"), GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE, \
0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0
.IF (eax==INVALID_HANDLE_VALUE)
invoke MessageBox, 0, CTEXT("Error!"), CTEXT("Can't create file"), MB_OK
invoke ExitProcess, -1
.ENDIF

; file handle is saved in EBX, since that's one of the registers that aren't trashed by
; API calls. SIZEOF is used instead of strlen, since SIZEOF is done at assemble-time instead
; of a runtime call to lstrlen.

mov ebx, eax
invoke WriteFile, ebx, ADDR buff,eax,ADDR chWritten,NULL

invoke CloseHandle, ebx



        .ELSE
            invoke WriteFile, hStdErr,ADDR errCreate,SIZEOF errCreate-1,ADDR chWritten,NULL
        .ENDIF
        pop ebx
    .ELSE



; invoke WriteFile, hStdErr,ADDR usage,len_usage-1,ADDR chWritten,NULL
        push 0
        push offset 004030D8h
        push 7Fh
        push offset 00403000h
        push dword ptr ds:[4030D4h]
        call WriteFile

    .ENDIF
    pop esi
    invoke ExitProcess, NULL

;***************************************************************************************************

skip_arg proc pString:DWORD
    push esi
    mov esi,pString
    xor eax,eax
    xor ecx,ecx

    ;skip any initial whitespace
  @@:
    mov al,[esi+ecx]
    add ecx,1
    cmp al,' '      ;SPACE
    je @B
    cmp al,9        ;TAB
    je @B

    ;may start with a quote
    cmp al,'"'      ;QUOTE
    je @qt

    ;find end of argument
  @@:
    cmp al,' '      ;SPACE
    je @tail
    cmp al,9        ;TAB
    je @tail
    test al,al      ;end of string
    jz @fail
    mov al,[esi+ecx]
    add ecx,1
    jmp @B

  @qt:
    ;skip until closing quote
    mov al,[esi+ecx]
    add ecx,1
    test al,al
    jz @fail
    cmp al,'"'      ;QUOTE
    jne @qt

  @tail:
    ;skip any trailing whitespace
    mov al,[esi+ecx]
    add ecx,1
    cmp al,' '      ;SPACE
    je @tail
    cmp al,9        ;TAB
    je @tail
    test al,al      ;end of string
    jz @fail

    sub ecx,1
    mov eax,ecx
    pop esi
    ret

  @fail:
    mov eax,-1
    pop esi
    ret
skip_arg endp

end start

Have a great day,
                         Andy

Tedd

"%lums" is just a format-control string - it's actually "%lu" meaning the first parameter is a long-unsigned (dword, essentially) and "ms" is literally those characters, so you end up with something like "54321ms"
"%d" should also work in most cases, but it's signed so you could get negatives for large numbers.
Check the docs for wsprintf http://msdn.microsoft.com/en-us/library/ms647550%28VS.85%29.aspx

To split the time into hours/minutes/seconds, it should be simple enough to write a function to do the divisions and then format the numbers into the given buffer (big hint: <hrs:min:sec.msec> = "%02lu:%02lu:%02lu.%03lu")


Quote from: Magnum on June 05, 2010, 06:09:17 PM

; invoke WriteFile, hStdErr,ADDR usage,len_usage-1,ADDR chWritten,NULL
        push 0
        push offset 004030D8h
        push 7Fh
        push offset 00403000h
        push dword ptr ds:[4030D4h]
        call WriteFile

I would strongly advise against hard-coding memory addresses - they can easily change without any notice given to you. This is why we have symbolic names.
No snowflake in an avalanche feels responsible.

Magnum

Quote from: Tedd on June 05, 2010, 06:33:29 PM

Quote from: Magnum on June 05, 2010, 06:09:17 PM

; invoke WriteFile, hStdErr,ADDR usage,len_usage-1,ADDR chWritten,NULL
        push 0
        push offset 004030D8h
        push 7Fh
        push offset 00403000h
        push dword ptr ds:[4030D4h]
        call WriteFile

I would strongly advise against hard-coding memory addresses - they can easily change without any notice given to you. This is why we have symbolic names.

The original source was wrong(would not compile), so I used Ollydbg to come up with this.
Can you convert to the "Non-hard version."




Have a great day,
                         Andy

Magnum

I am trying to come up with the logic to convert milliseconds to
seconds,minutes, and hours.

This is what I have.
Alright, let me have it.  :bg

; seconds = milliseconds/1000
; minutes = seconds/60
; hours = minutes/60
; days = hours/24

Ex. 1,514,047 milliseconds

Divide result by 1000,

if less than 1, answer is in milliseconds
if greater than 1 or less than 60, answer is in seconds
if 60 or less than 3600, answer is in minutes
if 3600 or less than 216000, answer is in hours
Have a great day,
                         Andy

Ghandi

You could work it the other way also:

1. Take total time.
2. Divide by 3600000 : Quotient = Hours    (60 min * 60 sec * 1000 milliseconds)
3. Divide remainder by 60000 : Quotient = Minutes (60 sec * 1000 milliseconds)
4. Divide remainder by 1000 : Quotient = Seconds
5. Remainder = Milliseconds


If you really wanted, you could use (24 * 60 * 60 * 1000) to get a divisor to calculate days also, but beware overflowing as Tedd mentioned. Honestly though using milliseconds to calculate days isn't what i'd do but horses for courses.

HR,
Ghandi

Magnum

Ok, I will work on #1 first and get back.

Have a great day,
                         Andy

Ghandi

You will work on #1? But you already have the total time, you're getting it from GetProcessTimes, its just a matter of division. :D

HR,
Ghandi

Magnum

I meant #2.
Is the total time in this eax?


push eax
push OFFSET result

Have a great day,
                         Andy