summaryrefslogblamecommitdiffstats
path: root/main/common/memtrace.c
blob: 2b1cc302081289bf309165f5cf95f908b3a12f6c (plain) (tree)
1
2
3
4


                                                                           
  
















































                                                                           

                                                                          

                 









                                                                        

               








                                                                        







                                                                           
                                                                        







                                                                          












































































                                                                            




                 






                                                        




                                








                                               




                         




























                                     







                                                           




                               


                      

                                      
                       











                                        
      
     




                                

































































































                                                                          






                                
                                                




                      
              


      
/**************************************************************************
 *
 * Copyright (c) 2013 Alcatel-Lucent
 *
 * Alcatel Lucent licenses this file to You under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License.  A copy of the License is contained the
 * file LICENSE at the top level of this repository.
 * You may also obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 **************************************************************************
 *
 * memtrace.c:
 *
 * This file contains CLI and API code to support a simple memory trace
 * capability that allows application developers to call mon_memtrace()
 * with a "printf-like" formatted arglist and the formatted string is
 * put into a circular buffer in some allocated RAM space.
 * The circular buffer is established by "mtrace cfg" command, then all
 * subsequent calls to mon_memtrace() will have the formatted string
 * destined for that buffer.
 *
 * To keep the output formatted clean, the user of mon_memtrace() should
 * not include any line-feeds in the string.  Each time mon_memtrace() is
 * called, a line feed and sequence number is prepended to the string.
 * This allows the dump to simply run through the buffer using putchar().
 *
 * Both the mon_memtrace() API function and the dump facility in the CLI
 * will deal with buffer wrapping.
 *
 * Original author:     Ed Sutter (ed.sutter@alcatel-lucent.com)
 *
 */

#include "config.h"
#include <stdarg.h>
#include "stddefs.h"
#include "genlib.h"
#include "cli.h"

#if INCLUDE_MEMTRACE

#define MINBUFSIZE 512
#define MAXLINSIZE MINBUFSIZE/2

#define MODE_PRINT      (1<<0)      /* mtrace text is output to console */
#define MODE_NOWRAP     (1<<1)      /* when mtrace buffer fills, stop */

/* struct mtInfo:
 *  This structure is at the base of the memory space allocated for
 *  the print buffer.  The control structure is part of the print buffer
 *  because the print buffer is assumed to be outside of the bss area
 *  of the monitor; hence, if a reset occurs and the monitor clears out
 *  its bss space, this structure will still be accessible and contain
 *  the data prior to the reset.
 *  So, to initialize a memory trace buffer use...
 *       mtrace cfg BASE SIZE
 *  and to re-establish the trace after a reset, use...
 *       mtrace mip BASE
 */
struct mtInfo {
    char *base;     /* Base of ram space allocated for print buffer. */
    char *ptr;      /* Running pointer into circular print buffer. */
    char *end;      /* End of ram space allocated. */
    int size;       /* Size of space originally allocated for mtrace. */
    int off;        /* Set if tracing is disabled. */
    int sno;        /* Sequence number of Mtrace() call. */
    int wrap;       /* Wrap counter. */
    int mode;       /* See MODE_XXX bits above. */
    int reentered;  /* Reentry counter. */
};

static struct mtInfo *Mip;

/* Mtrace():
 * Memory trace... This function can be used to place some trace statements
 * (readable text) in some memory location specified by the
 * setting of mtracebuf.  This was originally written for debugging Xmodem
 * because you can't use printf() since the protocol is using the serial
 * port.  I have since pulled it out of the Xmodem.c file and placed it in
 * generally accessible space so that it can be made available to the
 * application code and other monitor code.
 */

int
Mtrace(char *fmt,...)
{
    static  int inMtraceNow;
    int len;
    char *eolp;
    va_list argp;

    /* Mtrace not configured or disabled, so just return.
     */
    if(!Mip || Mip->off) {
        return(0);
    }

    /* This may be called from interrupt and/or non-interrupt space of
     * an application, so we must deal with possible reentrancy here.
     */
    if(inMtraceNow) {
        Mip->reentered++;
        return(0);
    }

    inMtraceNow = 1;

    Mip->ptr += snprintf(Mip->ptr,MAXLINSIZE,"\n<%04d> ",Mip->sno++);

    va_start(argp,fmt);
    len = vsnprintf(Mip->ptr,MAXLINSIZE,fmt,argp);
    va_end(argp);

    /* Strip all CR/LFs from the incoming string.
     * The incoming string can have CR/LFs in it; however, they are stripped
     * so that the format of the dump is stable (one line per Mtrace call).
     * Notice that the top line of this function inserts a newline ahead
     * of the sequence number; hence, additional CR/LFs in the text would
     * just confuse the output.
     */
    eolp = Mip->ptr;
    while(*eolp) {
        if((*eolp == '\r') || (*eolp == '\n')) {
            strcpy(eolp,eolp+1);
            len--;
        } else {
            eolp++;
        }
    }

    /* If print flag is set, then dump to the console...
     */
    if(Mip->mode & MODE_PRINT) {
        int i;
        for(i=0; i<len; i++) {
            putchar(*Mip->ptr++);
        }
        putchar('\n');
    } else {
        Mip->ptr += len;
    }

    if(Mip->ptr >= Mip->end) {
        Mip->ptr = Mip->base;
        if(Mip->mode & MODE_NOWRAP) {
            Mip->off = 1;
        } else {
            Mip->wrap++;
        }
    }

    /* Flush the d-cache of the mtrace buffer and Mip structure after each
     * transfer...
     * This is important because if this is being accessed from an
     * application that has d-cache enabled, then the hardware is reset,
     * there is a chance that the data written was in cache and would be
     * lost.
     */
    flushDcache((char *)Mip,sizeof(struct mtInfo));
    flushDcache((char *)Mip->base,Mip->end - Mip->base);

    inMtraceNow = 0;
    return(len);
}

void
MtraceReset(void)
{
    Mip->ptr = Mip->base;
    Mip->sno = 1;
    Mip->wrap = 0;
    Mip->off = 0;
    Mip->mode = 0;
    Mip->reentered = 0;
    memset(Mip->base,0,Mip->size-sizeof(struct mtInfo));
}

void
MtraceInit(char *base, int size)
{
    if(size < MINBUFSIZE) {
        return;
    }

    Mip = (struct mtInfo *)base;
    Mip->base = base + sizeof(struct mtInfo);
    Mip->size = size;
    Mip->end = (Mip->base + size - MAXLINSIZE);
    MtraceReset();
}

char *
mDump(char *bp, int more)
{
    int line;

    line = 0;

    while((bp < Mip->end) && (*bp)) {
        putchar(*bp);
        if(more && (*bp == '\n')) {
            if(++line == 24) {
                line = 0;
                if(!More()) {
                    return(0);
                }
            }
        }
        bp++;
    }
    while(*bp) {
        putchar(*bp);
        if(more && (*bp == '\n')) {
            if(++line == 24) {
                line = 0;
                if(!More()) {
                    return(0);
                }
            }
        }
        bp++;
    }
    return(bp);
}

/* If Mip pointer is configured, return 1; else return zero
 * and print an error message.
 */
static int
MipConfigured(void)
{
    if(Mip) {
        return(1);
    }
    printf("Not configured\n");
    return(0);
}

char *MtraceHelp[] = {
    "Configure/Dump memory trace.",
    "-[nm] {cmd} [cmd specific args]",
#if INCLUDE_VERBOSEHELP
    "Options:",
    " -m  enable 'more' flag for dump.",
    " -n  disable wrapping.",
    "Cmd:",
    " on",
    " off",
    " dump",
    " pron",
    " reset",
    " log {msg}",
    " mip {base}",
    " cfg [{base} {size}]",
#endif
    0
};

int
MtraceCmd(int argc,char *argv[])
{
    char    *bp;
    int     more, opt, nowrap;

    more = 0;
    nowrap = 0;
    while((opt=getopt(argc,argv,"nm")) != -1) {
        switch(opt) {
        case 'n':
            nowrap = 1;
            break;
        case 'm':
            more = 1;
            break;
        default:
            return(CMD_PARAM_ERROR);
        }
    }

    if(argc <= optind) {
        return(CMD_PARAM_ERROR);
    }

    if(!strcmp(argv[optind],"cfg")) {
        if(argc == optind + 3) {
            MtraceInit((char *)strtoul(argv[optind+1],0,0),
                       strtoul(argv[optind+2],0,0));
            if(nowrap) {
                Mip->mode |= MODE_NOWRAP;
            }
        } else if(argc == optind + 1) {
            if(MipConfigured()) {
                printf("Base: 0x%lx, End: 0x%lx\n",
                       (ulong)Mip->base,(ulong)Mip->end);
                printf("Ptr:  0x%lx, Sno: %d\n",(ulong)Mip->ptr,Mip->sno);
                printf("Wrap: %d\n",Mip->wrap);
            }
        } else {
            return(CMD_PARAM_ERROR);
        }
    } else if(!strcmp(argv[optind],"on")) {
        if(MipConfigured()) {
            Mip->mode &= ~MODE_PRINT;
            Mip->off = 0;
        }
    } else if(!strcmp(argv[optind],"pron")) {
        if(MipConfigured()) {
            Mip->mode |= MODE_PRINT;
            Mip->off = 0;
        }
    } else if(!strcmp(argv[optind],"off")) {
        if(MipConfigured()) {
            Mip->off = 1;
        }
    } else if(!strcmp(argv[optind],"reset")) {
        if(MipConfigured()) {
            MtraceReset();
        }
    } else if(!strcmp(argv[optind],"mip")) {
        if(argc != optind + 2) {
            return(CMD_PARAM_ERROR);
        }
        Mip = (struct mtInfo *)strtoul(argv[optind+1],0,0);
    } else if(!strcmp(argv[optind],"log")) {
        if(argc != optind + 2) {
            return(CMD_PARAM_ERROR);
        }
        Mtrace(argv[optind+1]);
    } else if(!strcmp(argv[optind],"dump")) {
        if(MipConfigured()) {
            if(Mip->reentered) {
                printf("Reentry count: %d\n",Mip->reentered);
            }
            if(Mip->wrap) {
                printf("Buffer wrapped...\n");
                bp =  Mip->ptr;
                while(bp < Mip->end) {
                    if(*bp == '\n') {
                        bp = mDump(bp,more);
                        break;
                    }
                    bp++;
                }
                if(bp) {
                    bp =  Mip->base;
                    while(bp < Mip->ptr) {
                        putchar(*bp++);
                    }
                }
            } else {
                bp = mDump(Mip->base,more);
            }
            printf("\n\n");
        }
    } else {
        return(CMD_PARAM_ERROR);
    }

    return(CMD_SUCCESS);
}

#else

void
MtraceInit(char *base, int size)
{
    printf("Mtrace() facility not built in.\n");
}

int
Mtrace(char *fmt, ...)
{
    return(0);
}

#endif