/************************************************************************** * * 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 #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; iptr++); } 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