News:

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

echo procedure name

Started by korte, January 23, 2009, 04:00:27 PM

Previous topic - Next topic

Jimg

Must be wrong link, that just sends me to msdn.

EDIT:   oops, I see now that is indeed by Zooba.  Must be the correct link.

There they say "Here is a sample prologue macro which provides basic frame functionality".
That implies it is not the actual PROLOGUEDEF, just an example.  I'm only concerned with replacing the prologue and not covering all the possibilities like pascal, c, syscall, etc.

Playing around, I stripped down your prologue and got this to work:

.NOLIST
.NOCREF
.NOLISTMACRO
.686p
.mmx
.xmm
.model flat,stdcall
option casemap:none
assume fs:nothing
include windows.inc
include kernel32.inc
includelib kernel32.lib
include user32.inc
includelib user32.lib

xProlog MACRO procname, flags, parambytes, localbytes, reglist, userparams
LOCAL r,fforce,fseh
    fforce = 0
    for r,<userparams>
        ifidni <&r>,<FORCEFRAME>
            fforce = 1
        elseifidni <&r>,<FORCENOFRAME>
            fforce = 2
        endif
    endm
    IF (parambytes+localbytes) NE 0
        if fforce ne 2
            push ebp
            mov ebp,esp
        endif
        IF localbytes NE 0
            add esp,-localbytes
        ENDIF
    elseif fforce eq 1
        push ebp
        mov ebp,esp
    ENDIF
    IFNB <reglist>
        FOR r, reglist
            push r
        ENDM
    ENDIF
    xprocname textequ <&procname>
    EXITM %localbytes
ENDM
OPTION PROLOGUE:xProlog

.LIST

.code
.listall
TestProc proc
nop
%echo testing xprocname

.data   ; just to test with messagebox
%xxproc db "&xprocname&",0
.code
invoke MessageBox,0,addr xxproc,0,0
ret
TestProc endp

start:
call TestProc
invoke ExitProcess,0

end start


For some reason, xprocname is only available after the first instruction so I had to insert a nop.  I can't see any reason for that.



jj2007

That works at runtime, like my earlier post above, and fails at assembly time, i.e. the echos come in the wrong order, as shown below. Where do you see a practical application?


.NOLIST
.NOCREF
.NOLISTMACRO
.686p
.mmx
.xmm
.model flat,stdcall
option casemap:none
assume fs:nothing
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib

xProlog MACRO procname, flags, parambytes, localbytes, reglist, userparams
LOCAL r,fforce,fseh
    fforce = 0
    for r,<userparams>
        ifidni <&r>,<FORCEFRAME>
            fforce = 1
        elseifidni <&r>,<FORCENOFRAME>
            fforce = 2
        endif
    endm
    IF (parambytes+localbytes) NE 0
        if fforce ne 2
            push ebp
            mov ebp,esp
        endif
        IF localbytes NE 0
            add esp,-localbytes
        ENDIF
    elseif fforce eq 1
        push ebp
        mov ebp,esp
    ENDIF
    IFNB <reglist>
        FOR r, reglist
            push r
        ENDM
    ENDIF
    xprocname textequ <&procname>
    EXITM %localbytes
ENDM
OPTION PROLOGUE:xProlog

.LIST

.code
.listall

TestProc proc
nop
%echo testing xprocname

.data   ; just to test with messagebox
%xxproc db "&xprocname&",0
.code
invoke MessageBox,0,addr xxproc,0,0
ret
TestProc endp


TestBProc proc
nop
%echo testing xprocname

.data   ; just to test with messagebox
%x1proc db "&xprocname&",0
.code
invoke MessageBox,0,addr x1proc,0,0
ret
TestBProc endp


TestCProc proc
nop
%echo testing xprocname

.data   ; just to test with messagebox
%x2proc db "&xprocname&",0
.code
invoke MessageBox,0,addr x2proc,0,0
ret
TestCProc endp

start:
call TestBProc
call TestProc
call TestCProc
; .err
invoke ExitProcess,0

end start

Jimg

The only option I can see that is obviously missing is LOADDS, which I would never use, but would want to support.

Jimg

jj-

I don't see what you are saying.  If you look at the output listing, the procs are listed in the order that they appear.  At execution time, the messageboxes show in the order the procs were executed.  The advantage over what you posted earlier is that you don't have to put anything in the proc that contains any part of the proc name, e.g.  CurProcCt = 1

So if you want to trace, you only have to add to each proc the same macro statement, e.g.   TRACELOG,   you don't have to manually copy each proc name to pass to a macro.  I've done this in a large program and it's really a pain to copy all the proc names to pass them to a macro.

Jimg

For example, this is what I use now:

PaintGrid proc
    trlog PaintGrid


where trlog=

trlog macro pname,lev:vararg    ;; print each proc name in log as executed
    routine textequ <*pname>
    if Trace and PrintLog
        ifb <lev>
            inc indent
            indentflag=1
        else
            indentflag=0
        endif
        %plogm "&routine"
    endif
EndM


the other support macros are:
if PrintLog
include \WinAsm\Progs\AllMyStuff\PrtLog.inc
indentflag=1
endif
plog macro args:vararg
if PrintLog
plogm args
endif
EndM

dedent macro lev:vararg
    if Trace and PrintLog
        ifb <lev>
            dec indent  ;; do not affect carry flag
        endif
    endif
EndM


prtlog is too big to show in it's entirety here, but it starts-

prtlog proto umsg:dword,cmt:dword,hctrl:dword,DoIndent:dword  ; umsg=-1 means just print comment in cmt

plogm macro val,strgadd ;; print to log, String and Value
Local LocalText,pstr
pusha                         
if indentflag eq 1
indentamt textequ <indent>
else
indentamt textequ <0>
endif
ifb <strgadd>
quote substr <val>,1,1
ifidn quote,<">
.data
LocalText db val,0
.code                 
invoke prtlog,-1,offset LocalText,-1,indentamt
popa
exitm
endif
arg2 catstr <">,<val>,<">
.data
    LocalText db arg2,0
.code
    pstr equ <offset LocalText>
else                      
quote SUBSTR <strgadd>,1,1
IFIDN quote , <">  ;; if this is a quoted string
.data
LocalText db strgadd,0
.code
pstr equ <offset LocalText>
else
pstr equ strgadd
endif
endif
invoke prtlog,-1,pstr,val,indentamt
popa
EndM

.data?
hMsgLog dd ?
.data
ilfmtx db "%s%s=%li id=%li",0
lgfmtx db "%s%s %i",0
lgfmtx2 db "%s%s",0
firstprtmsg dd 1
LogName db ".\msg.log",0
unknownmsg db " unknown",0
indentblanks db 20 dup (" ")
endindents db 0
baddr dd 0
CloseLog = 99999999
.code 
prtlog proc umsg,cmt,hctrl,numindents  ; umsg=-1 means just print comment in cmt
local junkx:dword,llen:dword,prtdBuffx[512]:byte,dlgid:dword
pusha
m2m baddr,offset endindents ; assume no indents
mov eax,numindents
.if eax
sif eax > 0 && eax < 20
sub baddr,eax
.endif
.endif
mov dlgid,0
.if sdword ptr hctrl>0 && sdword ptr umsg != -1
inv GetDlgCtrlID,hctrl
mov dlgid,eax
.endif
.if firstprtmsg
mov firstprtmsg,0
inv CreateFile,addr LogName,GENERIC_WRITE,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0
mov hMsgLog,eax
.elseif umsg==WM_NCDESTROY || umsg==99999999 ; one of the last messages, close handle
inv CloseHandle,hMsgLog
mov hMsgLog,0
.endif
.if sdword ptr umsg==-1
.if hctrl==-1 && sdword ptr umsg != -1
inv wsprintf,addr prtdBuffx,addr lgfmtx2,baddr,cmt
.else
inv wsprintf,addr prtdBuffx,addr lgfmtx,baddr,cmt,hctrl
.endif
.else
.if umsg < 401h
mov eax,umsg ; get offset to text description for this message
shl eax,2
add eax,Table
mov eax,[eax]
.if byte ptr[eax]==0
mov eax,offset unknownmsg
.endif
.else
.if cmt!=0
mov eax,cmt
.else
mov eax,offset unknownmsg
.endif
.endif
inv wsprintf,addr prtdBuffx,addr ilfmtx,baddr,eax,umsg,dlgid
.endif
lea esi,prtdBuffx
mov ecx,1
.repeat
lodsb
inc ecx
.until al==0
mov dword ptr [esi-1],0a0dh
mov llen,ecx
.if hMsgLog==0 ; near the end, has already been closed, append to existing
inv CreateFile,addr LogName,GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0
mov hMsgLog,eax
inv SetFilePointer,eax,0,0,FILE_END
inv WriteFile,hMsgLog,addr prtdBuffx,llen,addr junkx,0
inv CloseHandle,hMsgLog
mov hMsgLog,0
.else
inv WriteFile,hMsgLog,addr prtdBuffx,llen,addr junkx,0
.endif
popa
ret
prtlog EndP

.data
wm0 db "WM_NULL",0
wm1 db "WM_CREATE",0
wm2 db "WM_DESTROY",0
wm3 db "WM_MOVE",0
wm4 db 0
wm5 db "WM_SIZE",0


followed by all the umsgs a dlgproc can get, so I can print out in the log each one as it is triggered.

This produces an output that looks like:

WM_ERASEBKGND=20 id=1005
*GridUProc -1
WM_PAINT=15 id=1006
*PaintGrid -1
*GridUProc -1
WM_NCPAINT=133 id=1006
*GridUProc -1
WM_ERASEBKGND=20 id=1006
*GridMsg -1
*pSetCellText -1
  *GetCellAdr -1
   *CheckSingleCol -1
   *CheckSingleRow -1
  *CreateAndPaint -1
   *CreateGrid -1
    *PaintGrid -1
*GridMsg -1
*pSetCellText -1
  *GetCellAdr -1
   *CheckSingleCol -1
   *CheckSingleRow -1
  *CreateAndPaint -1
   *CreateGrid -1
    *PaintGrid -1
*GridMsg -1
*pSetCellText -1
  *GetCellAdr -1
   *CheckSingleCol -1
   *CheckSingleRow -1
  *CreateAndPaint -1
   *CreateGrid -1
    *PaintGrid -1
*GridMsg -1
*pSetCellText -1
  *GetCellAdr -1
   *CheckSingleCol -1
   *CheckSingleRow -1
  *CreateAndPaint -1



drizz

#20
This should be close enough (functionally identical) to default "macros".

PrologueDef32 MACRO procname, flags, parambytes, localbytes, reglist, userparms
LOCAL ff
ff = 0
IFNB <userparms>
ff = @InStr(,<userparms>,<FORCEFRAME>)
ENDIF
IF (parambytes+localbytes) NE 0 OR ff
push ebp
mov ebp,esp
ENDIF
IF localbytes NE 0
add esp,-localbytes
ENDIF
FOR reg,reglist
push reg
ENDM
EXITM %localbytes
ENDM

EpilogueDef32 MACRO procname, flags, parambytes, localbytes, reglist, userparms
LOCAL ff
ff = 0
IFNB <userparms>
ff = @InStr(,<userparms>,<FORCEFRAME>)
ENDIF
FOR reg,reglist
pop reg
ENDM
IF (parambytes+localbytes NE 0) OR ff
leave ;; mov esp,ebp / pop ebp
ENDIF
;; flags and 10000b = true :: caller cleans stack
IF (flags and 10000b);;  OR ( parambytes EQ 0 )
ret
ELSE
ret parambytes
ENDIF
ENDM

PrologueDef16 MACRO procname, flags, parambytes, localbytes, reglist, userparms
LOCAL ff
ff = 0
IFNB <userparms>
ff = @InStr(,<userparms>,<FORCEFRAME>)
ENDIF
IF (parambytes+localbytes) NE 0 OR ff
push bp
mov bp,sp
ENDIF
IF localbytes NE 0
add sp,-localbytes
ENDIF
IFNB <userparms>
IF @InStr(1,<userparms>,<LOADDS>)
push ds            ;; Save DS and point it to DGROUP
mov ax,DGROUP
mov ds,ax
ENDIF
ENDIF
FOR reg,reglist
push reg
ENDM
EXITM %localbytes
ENDM

EpilogueDef16 MACRO procname, flags, parambytes, localbytes, reglist, userparms
LOCAL ff
FOR reg,reglist
pop reg
ENDM
ff = 0
IFNB <userparms>
ff = @InStr(,<userparms>,<FORCEFRAME>)
IF @InStr(,<userparms>,<LOADDS>)
pop ds ;; Restore DS
ENDIF
ENDIF
IF (parambytes+localbytes NE 0) OR ff
leave
ENDIF
;; flags and 10000b = true :: caller cleans stack
IF (flags and 10000b);;  OR ( parambytes EQ 0 )
ret
ELSE
ret parambytes
ENDIF
ENDM

IF @WordSize EQ DWORD
OPTION PROLOGUE:PrologueDef32
OPTION EPILOGUE:EpilogueDef32
ELSE
OPTION PROLOGUE:PrologueDef16
OPTION EPILOGUE:EpilogueDef16
ENDIF


EDIT: small typo and error
The truth cannot be learned ... it can only be recognized.

Jimg

Wow, that's much simpler.  Thanks alot :clap:

Do you see any problem with just using the prologue and letting masm use the default epilogue?

drizz

Quote from: Jimg on January 23, 2009, 10:04:23 PMDo you see any problem with just using the prologue and letting masm use the default epilogue?
FORCEFRAME will not work - masm fault. Everything else works.
The truth cannot be learned ... it can only be recognized.

Jimg

This is going to come in real handy and totally replace my current tracking/logging system.  Thank you very much Drizz. :U

jj2007

Quote from: Jimg on January 23, 2009, 07:24:48 PM
I've done this in a large program and it's really a pain to copy all the proc names to pass them to a macro.

You convinced me :U

japheth


Kuhl!

It's almost what JWasm does. There is one difference: if parmbytes + localbytes is 0, but VARARG is true, then JWasm will also create a stack frame.

Details in proc.c, function write_default_prologue():


    if( ( info->localsize != 0 ) || ( info->parasize != 0 ) || info->is_vararg || info->forceframe ) {

        if( ModuleInfo.Use32 ) {
            /* write 80386 prolog code
             PUSH EBP
             MOV  EBP, ESP
             SUB  ESP, localsize
            */
            AddLineQueue( "push ebp" );
            AddLineQueue( "mov ebp, esp" );
            if( info->localsize != 0 ) {
                if ( Options.masm_compat_gencode )
                    sprintf( buffer, "add esp, %d", -info->localsize );
                else
                    sprintf( buffer, "sub esp, %d", info->localsize );
                AddLineQueue( buffer );
            }
        } else {
            /* write 8086 prolog code
             PUSH BP
             MOV  BP, SP
             SUB  SP, localsize
             */
            AddLineQueue( "push bp" );
            AddLineQueue( "mov bp, sp" );
            if( info->localsize != 0 ) {
                if ( Options.masm_compat_gencode )
                    sprintf( buffer, "add sp, %d", -info->localsize );
                else
                    sprintf( buffer, "sub sp, %d", info->localsize );
                AddLineQueue( buffer );
            }
        }
    }

    if ( info->loadds ) {
        AddLineQueue( "push ds" );
        AddLineQueue( "mov ax, DGROUP" );
        if ( ModuleInfo.Use32 )
            AddLineQueue( "mov ds, eax" );
        else
            AddLineQueue( "mov ds, ax" );
    }

    /* Push the registers */
    if( info->regslist ) {
        strcpy( buffer, "push " );
        len = strlen( buffer );
        for( regist = info->regslist; regist; regist = regist->next ) {
            strcpy( buffer + len, regist->reg );
            AddLineQueue( buffer );
        }
    }


Jimg

Japheth-

How does one test for vararg in a prologue?

I ran a test using:

TestCProc proc SYSCALL x1:vararg
nop
ret
TestCProc endp

and the epilogue in this case does not do a  "mov esp, ebp" like normal.  Was this intentional?

EDIT:
Scratch that, I see that you never to that unless there are locals.  I remember a discussion about that somewhere.  Still want to know about vararg however.