blob: a9240961f8b01d7d7502212d5f6298b509c7b3e4 [file] [log] [blame]
/* natGCInfo.cc -- Native portion of support for creating heap dumps.
Copyright (C) 2007 Free Software Foundation
This file is part of libgcj.
This software is copyrighted work licensed under the terms of the
Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
details. */
#include <config.h>
#include <gcj/cni.h>
#include <gnu/gcj/util/GCInfo.h>
#ifdef HAVE_PROC_SELF_MAPS
//
// If /proc/self/maps does not exist we assume we are doomed and do nothing.
//
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
//
// Boehm GC includes.
//
#ifdef PACKAGE_NAME
#undef PACKAGE_NAME
#endif
#ifdef PACKAGE_STRING
#undef PACKAGE_STRING
#endif
#ifdef PACKAGE_TARNAME
#undef PACKAGE_TARNAME
#endif
#ifdef PACKAGE_VERSION
#undef PACKAGE_VERSION
#endif
#ifdef TRUE
#undef TRUE
#endif
#ifdef FALSE
#undef FALSE
#endif
extern "C" {
#include "private/dbg_mlc.h"
int GC_n_set_marks(hdr* hhdr);
ptr_t GC_clear_stack(ptr_t p);
extern int GC_gcj_kind;
extern int GC_gcj_debug_kind;
}
#endif
#ifdef HAVE_PROC_SELF_MAPS
static int gc_ok = 1;
struct gc_debug_info
{
int used;
int free;
int wasted;
int blocks;
FILE* fp;
};
static void
GC_print_debug_callback(hblk *h, word user_data)
{
hdr *hhdr = HDR(h);
size_t bytes = WORDS_TO_BYTES(hhdr -> hb_sz);
gc_debug_info *pinfo = (gc_debug_info *)user_data;
fprintf(pinfo->fp, "ptr = %#lx, kind = %d, size = %zd, marks = %d\n",
(unsigned long)h, hhdr->hb_obj_kind, bytes, GC_n_set_marks(hhdr));
}
/*
this next section of definitions shouldn't really be here.
copied from boehmgc/allchblk.c
*/
# define UNIQUE_THRESHOLD 32
# define HUGE_THRESHOLD 256
# define FL_COMPRESSION 8
# define N_HBLK_FLS (HUGE_THRESHOLD - UNIQUE_THRESHOLD)/FL_COMPRESSION \
+ UNIQUE_THRESHOLD
#ifndef USE_MUNMAP
extern "C" {
extern word GC_free_bytes[N_HBLK_FLS+1];
}
#endif
# ifdef USE_MUNMAP
# define IS_MAPPED(hhdr) (((hhdr) -> hb_flags & WAS_UNMAPPED) == 0)
# else /* !USE_MMAP */
# define IS_MAPPED(hhdr) 1
# endif /* USE_MUNMAP */
static void
GC_print_hblkfreelist_file(FILE *fp)
{
struct hblk * h;
word total_free = 0;
hdr * hhdr;
word sz;
int i;
fprintf(fp, "---------- Begin free map ----------\n");
for (i = 0; i <= N_HBLK_FLS; ++i)
{
h = GC_hblkfreelist[i];
#ifdef USE_MUNMAP
if (0 != h)
fprintf (fp, "Free list %ld:\n", (unsigned long)i);
#else
if (0 != h)
fprintf (fp, "Free list %ld (Total size %ld):\n",
(unsigned long)i,
(unsigned long)GC_free_bytes[i]);
#endif
while (h != 0)
{
hhdr = HDR(h);
sz = hhdr -> hb_sz;
fprintf (fp, "\t0x%lx size %lu ", (unsigned long)h,
(unsigned long)sz);
total_free += sz;
if (GC_is_black_listed (h, HBLKSIZE) != 0)
fprintf (fp, "start black listed\n");
else if (GC_is_black_listed(h, hhdr -> hb_sz) != 0)
fprintf (fp, "partially black listed\n");
else
fprintf (fp, "not black listed\n");
h = hhdr -> hb_next;
}
}
#ifndef USE_MUNMAP
if (total_free != GC_large_free_bytes)
{
fprintf (fp, "GC_large_free_bytes = %lu (INCONSISTENT!!)\n",
(unsigned long) GC_large_free_bytes);
}
#endif
fprintf (fp, "Total of %lu bytes on free list\n", (unsigned long)total_free);
fprintf (fp, "---------- End free map ----------\n");
}
static int GC_dump_count = 1;
static void
GC_print_debug_info_file(FILE* fp)
{
gc_debug_info info;
memset(&info, 0, sizeof info);
info.fp = fp;
if (gc_ok)
GC_gcollect();
fprintf(info.fp, "---------- Begin block map ----------\n");
GC_apply_to_all_blocks(GC_print_debug_callback, (word)(void*)(&info));
//fprintf(fp, "#Total used %d free %d wasted %d\n", info.used, info.free, info.wasted);
//fprintf(fp, "#Total blocks %d; %dK bytes\n", info.blocks, info.blocks*4);
fprintf(info.fp, "---------- End block map ----------\n");
//fprintf(fp, "\n***Free blocks:\n");
//GC_print_hblkfreelist();
}
namespace
{
class __attribute__ ((visibility ("hidden"))) GC_enumerator
{
public:
GC_enumerator(const char *name);
void enumerate();
private:
FILE* fp;
int bytes_fd;
void print_address_map();
void enumerate_callback(struct hblk *h);
static void enumerate_callback_adaptor(struct hblk *h, word dummy);
};
}
GC_enumerator::GC_enumerator(const char *name)
{
bytes_fd = -1;
fp = fopen (name, "w");
if (!fp)
{
printf ("GC_enumerator failed to open [%s]\n", name);
return;
}
printf ("GC_enumerator saving summary to [%s]\n", name);
// open heap file
char bytes_name[strlen(name) + 10];
sprintf (bytes_name, "%s.bytes", name);
bytes_fd = open (bytes_name, O_CREAT|O_TRUNC|O_WRONLY, 0666);
if (bytes_fd <= 0)
{
printf ("GC_enumerator failed to open [%s]\n", bytes_name);
return;
}
printf ("GC_enumerator saving heap contents to [%s]\n", bytes_name);
}
/*
sample format of /proc/self/maps
0063b000-00686000 rw-p 001fb000 03:01 81993 /avtrex/bin/dumppropapp
00686000-0072e000 rwxp 00000000 00:00 0
These are parsed below as:
start -end xxxx xxxxxxxx a:b xxxxxxxxxxxxxxx
*/
void
GC_enumerator::print_address_map()
{
FILE* fm;
char buffer[128];
fprintf(fp, "---------- Begin address map ----------\n");
fm = fopen("/proc/self/maps", "r");
if (fm == NULL)
{
#ifdef HAVE_STRERROR_R
if (0 == strerror_r (errno, buffer, sizeof buffer))
fputs (buffer, fp);
#else
fputs (strerror (errno), fp);
#endif
}
else
{
while (fgets (buffer, sizeof buffer, fm) != NULL)
{
fputs (buffer, fp);
char *dash = strchr(buffer, '-');
char *colon = strchr(buffer, ':');
if (dash && colon && ((ptrdiff_t)strlen(buffer) > (colon - buffer) + 2))
{
char *endp;
unsigned long start = strtoul(buffer, NULL, 16);
unsigned long end = strtoul(dash + 1, &endp, 16);
unsigned long a = strtoul(colon - 2, NULL, 16);
unsigned long b = strtoul(colon + 1, NULL, 16);
// If it is an anonymous mapping 00:00 and both readable
// and writeable then dump the contents of the mapping
// to the bytes file. Each block has a header of three
// unsigned longs:
// 0 - The number sizeof(unsigned long) to detect endianness and
// structure layout.
// 1 - The offset in VM.
// 2 - The Length in bytes.
// Followed by the bytes.
if (!a && !b && endp < colon && 'r' == endp[1] && 'w' == endp[2])
{
unsigned long t = sizeof(unsigned long);
write(bytes_fd, (void*)&t, sizeof(t));
write(bytes_fd, (void*)&start, sizeof(start));
t = end - start;
write(bytes_fd, (void*)&t, sizeof(t));
write(bytes_fd, (void*)start, (end - start));
}
}
}
fclose(fm);
}
fprintf(fp, "---------- End address map ----------\n");
fflush(fp);
}
void
GC_enumerator::enumerate()
{
print_address_map();
fprintf(fp, "---------- Begin object map ----------\n");
if (gc_ok)
GC_gcollect();
GC_apply_to_all_blocks(enumerate_callback_adaptor,
(word)(void*)(this));
fprintf(fp, "---------- End object map ----------\n");
fflush(fp);
GC_print_debug_info_file(fp);
fflush(fp);
GC_print_hblkfreelist_file(fp);
fflush(fp);
close(bytes_fd);
fclose(fp);
GC_clear_stack(0);
}
void
GC_enumerator::enumerate_callback_adaptor(struct hblk *h,
word dummy)
{
GC_enumerator* pinfo = (GC_enumerator*)dummy;
pinfo->enumerate_callback(h);
}
void
GC_enumerator::enumerate_callback(struct hblk *h)
{
hdr * hhdr = HDR(h);
size_t bytes = WORDS_TO_BYTES(hhdr->hb_sz);
int i;
for (i = 0; i == 0 || (i + bytes <= HBLKSIZE); i += bytes)
{
int inUse = mark_bit_from_hdr(hhdr,BYTES_TO_WORDS(i)); // in use
char *ptr = (char*)h+i; // address
int kind = hhdr->hb_obj_kind; // kind
void *klass = 0;
void *data = 0;
if (kind == GC_gcj_kind
|| kind == GC_gcj_debug_kind
|| kind == GC_gcj_debug_kind+1)
{
void* v = *(void **)ptr;
if (v)
{
klass = *(void **)v;
data = *(void **)(ptr + sizeof(void*));
}
}
if (inUse)
fprintf (fp, "used = %d, ptr = %#lx, size = %zd, kind = %d, "
"klass = %#lx, data = %#lx\n",
inUse, (unsigned long)ptr, bytes, kind,
(unsigned long)klass, (unsigned long)data);
}
}
/*
* Fill in a char[] with low bytes of the string characters. These
* methods may be called while an OutOfMemoryError is being thrown, so
* we cannot call nice java methods to get the encoding of the string.
*/
static void
J2A(::java::lang::String* str, char *dst)
{
jchar * pchars = JvGetStringChars(str);
jint len = str->length();
int i;
for (i=0; i<len; i++)
dst[i] = (char)pchars[i];
dst[i] = 0;
}
void
::gnu::gcj::util::GCInfo::dump0 (::java::lang::String * name)
{
char n[name->length() + 1];
J2A(name, n);
char temp[name->length() + 20];
sprintf(temp, "%s%03d", n, GC_dump_count++);
FILE* fp = fopen(temp, "w");
GC_print_debug_info_file(fp);
fclose(fp);
}
void
::gnu::gcj::util::GCInfo::enumerate0 (::java::lang::String * name)
{
char n[name->length() + 1];
J2A(name, n);
char temp[name->length() + 20];
sprintf(temp, "%s%03d", n, GC_dump_count++);
GC_enumerator x(temp);
x.enumerate();
}
static char *oomDumpName = NULL;
static void *
nomem_handler(size_t size)
{
if (oomDumpName)
{
char temp[strlen(oomDumpName) + 20];
sprintf(temp, "%s%03d", temp, GC_dump_count++);
printf("nomem_handler(%zd) called\n", size);
gc_ok--;
GC_enumerator x(temp);
x.enumerate();
gc_ok++;
}
return (void*)0;
}
void
::gnu::gcj::util::GCInfo::setOOMDump0 (::java::lang::String * name)
{
char *oldName = oomDumpName;
oomDumpName = NULL;
free (oldName);
if (NULL == name)
return;
char *n = (char *)malloc(name->length() + 1);
J2A(name, n);
oomDumpName = n;
GC_oom_fn = nomem_handler;
}
#else // HAVE_PROC_SELF_MAPS
void
::gnu::gcj::util::GCInfo::dump0 (::java::lang::String * name)
{
// Do nothing if dumping not supported.
}
void
::gnu::gcj::util::GCInfo::enumerate0 (::java::lang::String * name)
{
// Do nothing if dumping not supported.
}
void
::gnu::gcj::util::GCInfo::setOOMDump0 (::java::lang::String * name)
{
// Do nothing if dumping not supported.
}
#endif // HAVE_PROC_SELF_MAPS