Line 1... |
Line 1... |
/* mprofiler.c -- memory profiling utility
|
/* mprofiler.c -- memory profiling utility
|
|
|
Copyright (C) 2002 Marko Mlinar, markom@opencores.org
|
Copyright (C) 2002 Marko Mlinar, markom@opencores.org
|
|
Copyright (C) 1999 Damjan Lampret, lampret@opencores.org
|
|
Copyright (C) 2008 Embecosm Limited
|
|
|
This file is part of OpenRISC 1000 Architectural Simulator.
|
Contributor Jeremy Bennett <jeremy.bennett@embecosm.com>
|
|
|
This program is free software; you can redistribute it and/or modify
|
This file is part of Or1ksim, the OpenRISC 1000 Architectural Simulator.
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
This program is free software; you can redistribute it and/or modify it
|
(at your option) any later version.
|
under the terms of the GNU General Public License as published by the Free
|
|
Software Foundation; either version 3 of the License, or (at your option)
|
This program is distributed in the hope that it will be useful,
|
any later version.
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
GNU General Public License for more details.
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
You should have received a copy of the GNU General Public License
|
more details.
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
You should have received a copy of the GNU General Public License along
|
|
with this program. If not, see <http://www.gnu.org/licenses/>. */
|
/* Command line utility, that displays profiling information, generated
|
|
by or1ksim. (use --mprofile option at command line, when running or1ksim. */
|
/* This program is commented throughout in a fashion suitable for processing
|
|
with Doxygen. */
|
#if HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#if HAVE_MALLOC_H
|
|
#include <malloc.h> /* calloc, free */
|
|
#endif
|
|
#ifdef HAVE_INTTYPES_H
|
|
#include <inttypes.h>
|
|
#endif
|
|
|
|
|
/* Autoconf and/or portability configuration */
|
|
#include "config.h"
|
#include "port.h"
|
#include "port.h"
|
#include "arch.h"
|
|
#include "support/profile.h"
|
/* System includes */
|
|
#include <stdlib.h>
|
|
#include <regex.h>
|
|
|
|
/* Package includes */
|
#include "mprofiler.h"
|
#include "mprofiler.h"
|
#include "sim-config.h"
|
#include "sim-config.h"
|
|
#include "argtable2.h"
|
|
#include "support/profile.h"
|
|
|
struct memory_hash {
|
/* output modes */
|
|
#define MODE_DETAIL 0
|
|
#define MODE_PRETTY 1
|
|
#define MODE_ACCESS 2
|
|
#define MODE_WIDTH 3
|
|
|
|
/* Input buffer size */
|
|
#define BUF_SIZE 256
|
|
|
|
/* HASH */
|
|
#define HASH_SIZE 0x10000
|
|
#define HASH_FUNC(x) ((x) & 0xffff)
|
|
|
|
/*! Hash table data structure */
|
|
static struct memory_hash
|
|
{
|
struct memory_hash *next;
|
struct memory_hash *next;
|
oraddr_t addr;
|
oraddr_t addr;
|
unsigned long cnt[3]; /* Various counters */
|
unsigned long cnt[3]; /* Various counters */
|
} *hash[HASH_SIZE];
|
} *hash[HASH_SIZE];
|
|
|
/* Groups size -- how much addresses should be joined together */
|
/*! Groups size -- how much addresses should be joined together */
|
int group_bits = 2;
|
static int group_bits = 2;
|
|
|
/* Start address */
|
/*! Start address */
|
oraddr_t start_addr = 0;
|
static oraddr_t start_addr = 0;
|
|
|
/* End address */
|
/*! End address */
|
oraddr_t end_addr = 0xffffffff;
|
static oraddr_t end_addr = 0xffffffff;
|
|
|
/* File to read from */
|
/* File to read from */
|
static FILE *fprof = 0;
|
static FILE *fprof = 0;
|
|
|
void mp_help (void)
|
static void
|
{
|
hash_add (oraddr_t addr, int index)
|
PRINTF ("mprofiler <-d|-p|-a|-w> [-f filename] [-g group] from to\n");
|
|
PRINTF ("\t-d\t--detail\t\tdetailed output\n");
|
|
PRINTF ("\t-p\t--pretty\t\tpretty output\n");
|
|
PRINTF ("\t-a\t--access\t\toutput accesses only\n");
|
|
PRINTF ("\t-w\t--width\t\t\toutput by width\n");
|
|
PRINTF ("\t-f\t--filename filename\tspecify mprofile file [sim.mprofile]\n");
|
|
PRINTF ("\t-g\t--group bits\t\tgroup 2^bits successive\n");
|
|
PRINTF ("\t\t\t\t\taddresses together [2]\n");
|
|
PRINTF ("\t-h\t--help\t\t\toutput this screen\n");
|
|
}
|
|
|
|
void hash_add (oraddr_t addr, int index)
|
|
{
|
{
|
struct memory_hash *h = hash[HASH_FUNC(addr)];
|
struct memory_hash *h = hash[HASH_FUNC(addr)];
|
while (h && h->addr != addr) h = h->next;
|
while (h && h->addr != addr)
|
|
h = h->next;
|
|
|
if (!h) {
|
if (!h)
|
|
{
|
h = (struct memory_hash *)malloc (sizeof (struct memory_hash));
|
h = (struct memory_hash *)malloc (sizeof (struct memory_hash));
|
h->next = hash[HASH_FUNC(addr)];
|
h->next = hash[HASH_FUNC(addr)];
|
hash[HASH_FUNC(addr)] = h;
|
hash[HASH_FUNC(addr)] = h;
|
h->addr = addr;
|
h->addr = addr;
|
h->cnt[0] = h->cnt[1] = h->cnt[2] = 0;
|
h->cnt[0] = h->cnt[1] = h->cnt[2] = 0;
|
}
|
}
|
h->cnt[index]++;
|
h->cnt[index]++;
|
}
|
}
|
|
|
unsigned long hash_get (oraddr_t addr, int index)
|
|
|
static unsigned long
|
|
hash_get (oraddr_t addr, int index)
|
{
|
{
|
struct memory_hash *h = hash[HASH_FUNC(addr)];
|
struct memory_hash *h = hash[HASH_FUNC(addr)];
|
while (h && h->addr != addr) h = h->next;
|
while (h && h->addr != addr)
|
|
h = h->next;
|
|
|
if (!h) return 0;
|
if (!h)
|
|
return 0;
|
return h->cnt[index];
|
return h->cnt[index];
|
}
|
}
|
|
|
void init (void)
|
static void
|
|
init ()
|
{
|
{
|
int i;
|
int i;
|
for (i = 0; i < HASH_SIZE; i++)
|
for (i = 0; i < HASH_SIZE; i++)
|
hash[i] = NULL;
|
hash[i] = NULL;
|
}
|
}
|
|
|
void read_file (FILE *f, int mode)
|
static void
|
|
read_file (FILE * f, int mode)
|
{
|
{
|
struct mprofentry_struct buf[BUF_SIZE];
|
struct mprofentry_struct buf[BUF_SIZE];
|
int num_read;
|
int num_read;
|
do {
|
do
|
|
{
|
int i;
|
int i;
|
num_read = fread (buf, sizeof (struct mprofentry_struct), BUF_SIZE, f);
|
num_read = fread (buf, sizeof (struct mprofentry_struct), BUF_SIZE, f);
|
for (i = 0; i < num_read; i++) if (buf[i].addr >= start_addr && buf[i].addr <= end_addr) {
|
for (i = 0; i < num_read; i++)
|
|
if (buf[i].addr >= start_addr && buf[i].addr <= end_addr)
|
|
{
|
int index;
|
int index;
|
unsigned t = buf[i].type;
|
unsigned t = buf[i].type;
|
if (t > 64) {
|
if (t > 64)
|
|
{
|
PRINTF ("!");
|
PRINTF ("!");
|
t = 0;
|
t = 0;
|
}
|
}
|
if (mode == MODE_WIDTH) t >>= 3;
|
if (mode == MODE_WIDTH)
|
else t &= 0x7;
|
t >>= 3;
|
|
else
|
|
t &= 0x7;
|
|
|
switch (t) {
|
switch (t)
|
case 1: index = 0; break;
|
{
|
case 2: index = 1; break;
|
case 1:
|
case 4: index = 2; break;
|
index = 0;
|
|
break;
|
|
case 2:
|
|
index = 1;
|
|
break;
|
|
case 4:
|
|
index = 2;
|
|
break;
|
default:
|
default:
|
index = 0;
|
index = 0;
|
PRINTF ("!!!!");
|
PRINTF ("!!!!");
|
break;
|
break;
|
}
|
}
|
hash_add (buf[i].addr >> group_bits, index);
|
hash_add (buf[i].addr >> group_bits, index);
|
}
|
}
|
} while (num_read > 0);
|
}
|
|
while (num_read > 0);
|
}
|
}
|
|
|
static inline int nbits (unsigned long a)
|
static int
|
|
nbits (unsigned long a)
|
{
|
{
|
int cnt = 0;
|
int cnt = 0;
|
int b = a;
|
int b = a;
|
if (!a) return 0;
|
if (!a)
|
|
return 0;
|
|
|
while (a) a >>= 1, cnt++;
|
while (a)
|
|
a >>= 1, cnt++;
|
if (cnt > 1 && ((b >> (cnt - 2)) & 1))
|
if (cnt > 1 && ((b >> (cnt - 2)) & 1))
|
cnt = cnt * 2 + 1;
|
cnt = cnt * 2 + 1;
|
else
|
else
|
cnt *= 2;
|
cnt *= 2;
|
|
|
return cnt - 1;
|
return cnt - 1;
|
}
|
}
|
|
|
void printout (int mode)
|
static void
|
|
printout (int mode)
|
{
|
{
|
oraddr_t addr = start_addr & ~((1 << group_bits) - 1);
|
oraddr_t addr = start_addr & ~((1 << group_bits) - 1);
|
PRINTF ("start = %"PRIxADDR" (%"PRIxADDR"); end = %"PRIxADDR"; group_bits = %08x\n", start_addr, addr, end_addr, (1 << group_bits) - 1);
|
PRINTF ("start = %" PRIxADDR " (%" PRIxADDR "); end = %" PRIxADDR
|
for (; addr <= end_addr; addr += (1 << group_bits)) {
|
"; group_bits = %08x\n", start_addr, addr, end_addr,
|
|
(1 << group_bits) - 1);
|
|
for (; addr <= end_addr; addr += (1 << group_bits))
|
|
{
|
int i;
|
int i;
|
unsigned long a = hash_get (addr >> group_bits, 0);
|
unsigned long a = hash_get (addr >> group_bits, 0);
|
unsigned long b = hash_get (addr >> group_bits, 1);
|
unsigned long b = hash_get (addr >> group_bits, 1);
|
unsigned long c = hash_get (addr >> group_bits, 2);
|
unsigned long c = hash_get (addr >> group_bits, 2);
|
PRINTF ("%"PRIxADDR":", addr);
|
PRINTF ("%"PRIxADDR":", addr);
|
switch (mode) {
|
switch (mode)
|
|
{
|
case MODE_DETAIL:
|
case MODE_DETAIL:
|
if (a) PRINTF (" %10li R", a);
|
if (a)
|
else PRINTF (" R");
|
PRINTF (" %10li R", a);
|
if (b) PRINTF (" %10li W", b);
|
else
|
else PRINTF (" W");
|
PRINTF (" R");
|
if (c) PRINTF (" %10li F", c);
|
if (b)
|
else PRINTF (" F");
|
PRINTF (" %10li W", b);
|
|
else
|
|
PRINTF (" W");
|
|
if (c)
|
|
PRINTF (" %10li F", c);
|
|
else
|
|
PRINTF (" F");
|
break;
|
break;
|
case MODE_ACCESS:
|
case MODE_ACCESS:
|
PRINTF (" %10li", a + b + c);
|
PRINTF (" %10li", a + b + c);
|
break;
|
break;
|
case MODE_PRETTY:
|
case MODE_PRETTY:
|
PRINTF (" %10li ", a + b + c);
|
PRINTF (" %10li ", a + b + c);
|
for (i = 0; i < nbits (a + b + c); i++)
|
for (i = 0; i < nbits (a + b + c); i++)
|
PRINTF ("#");
|
PRINTF ("#");
|
#if 0
|
|
for (; i < 64; i++)
|
|
PRINTF (".");
|
|
#endif
|
|
break;
|
break;
|
case MODE_WIDTH:
|
case MODE_WIDTH:
|
if (a) PRINTF (" %10li B", a);
|
if (a)
|
else PRINTF (" B");
|
PRINTF (" %10li B", a);
|
if (b) PRINTF (" %10li H", b);
|
else
|
else PRINTF (" H");
|
PRINTF (" B");
|
if (c) PRINTF (" %10li W", c);
|
if (b)
|
else PRINTF (" W");
|
PRINTF (" %10li H", b);
|
|
else
|
|
PRINTF (" H");
|
|
if (c)
|
|
PRINTF (" %10li W", c);
|
|
else
|
|
PRINTF (" W");
|
break;
|
break;
|
}
|
}
|
PRINTF ("\n");
|
PRINTF ("\n");
|
if (addr >= addr + (1 << group_bits)) break; /* Overflow? */
|
if (addr >= addr + (1 << group_bits))
|
|
break; /* Overflow? */
|
}
|
}
|
}
|
}
|
|
|
int main_mprofiler (int argc, char *argv[])
|
/*---------------------------------------------------------------------------*/
|
|
/*! Parse the arguments for the profiling utility
|
|
|
|
Updated by Jeremy Bennett to use argtable2. Also has an option just to
|
|
print help, for use with the CLI.
|
|
|
|
@param[in] argc Number of command args
|
|
@param[in] argv Vector of the command args
|
|
@param[in] just_help If 1 (true), ignore argc & argv and just print out
|
|
the help message without parsing args
|
|
|
|
@return 0 on success, 1 on failure */
|
|
/*---------------------------------------------------------------------------*/
|
|
int
|
|
main_mprofiler (int argc, char *argv[], int just_help)
|
{
|
{
|
char fmprofname[50] = "sim.mprofile";
|
struct arg_lit *vercop;
|
int param = 0;
|
struct arg_lit *help;
|
|
struct arg_rex *mode_arg;
|
|
struct arg_int *group;
|
|
struct arg_file *prof_file;
|
|
struct arg_int *from;
|
|
struct arg_int *to;
|
|
struct arg_end *end;
|
|
|
|
void *argtab[8];
|
|
int nerrors;
|
|
|
int mode = MODE_DETAIL;
|
int mode = MODE_DETAIL;
|
|
|
argv++; argc--;
|
/* Specify each argument, with fallback values */
|
while (argc > 0) {
|
vercop = arg_lit0 ("v", "version", "version and copyright notice");
|
if (!strcmp(argv[0], "-d") || !strcmp(argv[0], "--detail")) {
|
help = arg_lit0 ("h", "help", "print this help message");
|
mode = MODE_DETAIL;
|
mode_arg = arg_rex0 ("m", "mode",
|
argv++; argc--;
|
"(detailed|d)|(pretty|p)|(access|a)|"
|
} else if (!strcmp(argv[0], "-p") || !strcmp(argv[0], "--pretty")) {
|
"(width|w)", "<m>", REG_ICASE,
|
mode = MODE_PRETTY;
|
"Output mode (detailed, pretty, access " "or width)");
|
argv++; argc--;
|
mode_arg->sval[0] = "detailed";
|
} else if (!strcmp(argv[0], "-a") || !strcmp(argv[0], "--access")) {
|
group = arg_int0 ("g", "group", "<n>",
|
mode = MODE_ACCESS;
|
"group 2^n bits successive addresses " "together");
|
argv++; argc--;
|
group->ival[0] = 0;
|
} else if (!strcmp(argv[0], "-w") || !strcmp(argv[0], "--width")) {
|
prof_file = arg_file0 ("f", "filename", "<file>",
|
mode = MODE_WIDTH;
|
"data file to analyse (default " "sim.mprofile)");
|
argv++; argc--;
|
prof_file->filename[0] = "sim.mprofile";
|
} else if (!strcmp(argv[0], "-g") || !strcmp(argv[0], "--group")) {
|
from = arg_int1 (NULL, NULL, "<from>", "start address");
|
argv++; argc--;
|
to = arg_int1 (NULL, NULL, "<to>", "end address");
|
group_bits = strtoul (argv[0], NULL, 0);
|
end = arg_end (20);
|
argv++; argc--;
|
|
} else if (!strcmp(argv[0], "-h") || !strcmp(argv[0], "--help")) {
|
/* Set up the argument table */
|
mp_help ();
|
argtab[0] = vercop;
|
|
argtab[1] = help;
|
|
argtab[2] = mode_arg;
|
|
argtab[3] = group;
|
|
argtab[4] = prof_file;
|
|
argtab[5] = from;
|
|
argtab[6] = to;
|
|
argtab[7] = end;
|
|
|
|
/* If we are just asked for a help message, then we don't parse the
|
|
args. This is used to implement the help function from the CLI. */
|
|
if (just_help)
|
|
{
|
|
printf ("mprofile");
|
|
arg_print_syntax (stdout, argtab, "\n");
|
|
arg_print_glossary (stdout, argtab, " %-25s %s\n");
|
|
|
|
arg_freetable (argtab, sizeof (argtab) / sizeof (argtab[0]));
|
return 0;
|
return 0;
|
} else if (!strcmp(argv[0], "-f") || !strcmp(argv[0], "--filename")) {
|
|
argv++; argc--;
|
|
strcpy (&fmprofname[0], argv[0]);
|
|
argv++; argc--;
|
|
} else {
|
|
switch (param) {
|
|
case 0:
|
|
start_addr = strtoul (argv[0], NULL, 0);
|
|
break;
|
|
case 1:
|
|
end_addr = strtoul (argv[0], NULL, 0);
|
|
break;
|
|
default:
|
|
fprintf (stderr, "Invalid number of parameters.\n");
|
|
return -1;
|
|
}
|
}
|
argv++; argc--; param++;
|
|
|
/* Parse */
|
|
nerrors = arg_parse (argc, argv, argtab);
|
|
|
|
/* Special case here is if help or version is specified, we ignore any other
|
|
errors and just print the help or version information and then give up. */
|
|
if (vercop->count > 0)
|
|
{
|
|
PRINTF ("OpenRISC 1000 Memory Profiling Utility, version %s\n",
|
|
PACKAGE_VERSION);
|
|
|
|
arg_freetable (argtab, sizeof (argtab) / sizeof (argtab[0]));
|
|
return 0;
|
|
}
|
|
|
|
if (help->count > 0)
|
|
{
|
|
printf ("Usage: %s ", argv[0]);
|
|
arg_print_syntax (stdout, argtab, "\n");
|
|
arg_print_glossary (stdout, argtab, " %-25s %s\n");
|
|
|
|
arg_freetable (argtab, sizeof (argtab) / sizeof (argtab[0]));
|
|
return 0;
|
|
}
|
|
|
|
/* Deal with any errors */
|
|
if (0 != nerrors)
|
|
{
|
|
arg_print_errors (stderr, end, "mprofile");
|
|
fprintf (stderr, "Usage: %s ", argv[0]);
|
|
arg_print_syntaxv (stderr, argtab, "\n");
|
|
|
|
arg_freetable (argtab, sizeof (argtab) / sizeof (argtab[0]));
|
|
return 1;
|
|
}
|
|
|
|
/* If version or help is requested, that is all that is printed out */
|
|
/* Sort out the mode */
|
|
if ((0 == strcmp (mode_arg->sval[0], "detail")) ||
|
|
(0 == strcmp (mode_arg->sval[0], "d")))
|
|
{
|
|
mode = MODE_DETAIL;
|
|
}
|
|
else if ((0 == strcmp (mode_arg->sval[0], "pretty")) ||
|
|
(0 == strcmp (mode_arg->sval[0], "p")))
|
|
{
|
|
mode = MODE_DETAIL;
|
|
}
|
|
else if ((0 == strcmp (mode_arg->sval[0], "access")) ||
|
|
(0 == strcmp (mode_arg->sval[0], "a")))
|
|
{
|
|
mode = MODE_DETAIL;
|
|
}
|
|
else if ((0 == strcmp (mode_arg->sval[0], "width")) ||
|
|
(0 == strcmp (mode_arg->sval[0], "w")))
|
|
{
|
|
mode = MODE_DETAIL;
|
}
|
}
|
|
else
|
|
{
|
|
fprintf (stderr, "Impossible mode: %s\n", mode_arg->sval[0]);
|
}
|
}
|
|
|
fprof = fopen (fmprofname, "rm");
|
/* Group bits */
|
|
group_bits = group->ival[0];
|
|
|
|
/* Start and end addresses */
|
|
start_addr = from->ival[0];
|
|
end_addr = to->ival[0];
|
|
|
|
/* Get the profile */
|
|
fprof = fopen (prof_file->filename[0], "rm");
|
|
|
if (!fprof) {
|
if (!fprof)
|
fprintf (stderr, "Cannot open profile file: %s\n", fmprofname);
|
{
|
|
fprintf (stderr, "Cannot open profile file: %s\n",
|
|
prof_file->filename[0]);
|
|
|
|
arg_freetable (argtab, sizeof (argtab) / sizeof (argtab[0]));
|
return 1;
|
return 1;
|
}
|
}
|
|
|
|
/* Finished with the args */
|
|
arg_freetable (argtab, sizeof (argtab) / sizeof (argtab[0]));
|
|
|
init ();
|
init ();
|
read_file (fprof, mode);
|
read_file (fprof, mode);
|
fclose (fprof);
|
fclose (fprof);
|
printout (mode);
|
printout (mode);
|
return 0;
|
return 0;
|
}
|
|
|
} /* main_mprofiler () */
|
|
|
No newline at end of file
|
No newline at end of file
|