diff options
author | Amar Takhar <amar@rtems.org> | 2015-04-16 15:26:21 -0400 |
---|---|---|
committer | Amar Takhar <amar@rtems.org> | 2015-04-16 15:26:21 -0400 |
commit | 87db514f2dfff5ad67863a30f075b718706de346 (patch) | |
tree | 31edc1b1700de9f3d4825822534928f8a213d53f /main/common/memtrace.c | |
download | umon-87db514f2dfff5ad67863a30f075b718706de346.tar.bz2 |
Initial commit of the umon repository.
Prior to this three changes were made:
* Remove umon_ prefix from parent directories.
* Collapse main/target/ into main/
* Remove ports/template/flashtest.scr.ucon script.
Diffstat (limited to 'main/common/memtrace.c')
-rw-r--r-- | main/common/memtrace.c | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/main/common/memtrace.c b/main/common/memtrace.c new file mode 100644 index 0000000..2962007 --- /dev/null +++ b/main/common/memtrace.c @@ -0,0 +1,379 @@ +/************************************************************************** + * + * 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 |