blob: e1c346a0e348c1ca30d603a64e804323b72731b8 [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "TraceLogging.h"
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
using namespace js;
#ifndef TRACE_LOG_DIR
# if defined(_WIN32)
# define TRACE_LOG_DIR ""
# else
# define TRACE_LOG_DIR "/tmp/"
# endif
#endif
#if defined(__i386__)
static __inline__ uint64_t
js::rdtsc(void)
{
uint64_t x;
__asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
return x;
}
#elif defined(__x86_64__)
static __inline__ uint64_t
js::rdtsc(void)
{
unsigned hi, lo;
__asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
return ( (uint64_t)lo)|( ((uint64_t)hi)<<32 );
}
#elif defined(__powerpc__)
static __inline__ uint64_t
js::rdtsc(void)
{
uint64_t result=0;
uint32_t upper, lower,tmp;
__asm__ volatile(
"0: \n"
"\tmftbu %0 \n"
"\tmftb %1 \n"
"\tmftbu %2 \n"
"\tcmpw %2,%0 \n"
"\tbne 0b \n"
: "=r"(upper),"=r"(lower),"=r"(tmp)
);
result = upper;
result = result<<32;
result = result|lower;
return(result);
}
#else
static __inline__ uint64_t
rdtsc(void)
{
return 0;
}
#endif
const char* const TraceLogging::type_name[] = {
"start,ion_compile",
"stop,ion_compile",
"start,ion_cannon",
"stop,ion_cannon",
"stop,ion_cannon_bailout",
"start,ion_side_cannon",
"stop,ion_side_cannon",
"stop,ion_side_cannon_bailout",
"start,yarr_jit_execute",
"stop,yarr_jit_execute",
"start,jm_safepoint",
"stop,jm_safepoint",
"start,jm_normal",
"stop,jm_normal",
"start,jm_compile",
"stop,jm_compile",
"start,gc",
"stop,gc",
"start,interpreter",
"stop,interpreter"
};
TraceLogging* TraceLogging::_defaultLogger = NULL;
TraceLogging::TraceLogging()
: loggingTime(0),
entries(NULL),
curEntry(0),
numEntries(1000000),
fileno(0),
out(NULL)
{
}
TraceLogging::~TraceLogging()
{
if (out != NULL) {
fclose(out);
out = NULL;
}
if (entries != NULL) {
flush();
js_free(entries);
entries = NULL;
}
}
void
TraceLogging::grow()
{
Entry* nentries = (Entry*) js_realloc(entries, numEntries*2*sizeof(Entry));
// Allocating a bigger array failed.
// Keep using the current storage, but remove all entries by flushing them.
if (nentries == NULL) {
flush();
return;
}
entries = nentries;
numEntries *= 2;
}
void
TraceLogging::log(Type type, const char* file, unsigned int lineno)
{
uint64_t now = rdtsc();
// Create array containing the entries if not existing.
if (entries == NULL) {
entries = (Entry*) js_malloc(numEntries*sizeof(Entry));
if (entries == NULL)
return;
}
// Copy the logging information,
// because original could already be freed before writing the log file.
char *copy = NULL;
if (file != NULL)
copy = strdup(file);
entries[curEntry++] = Entry(now - loggingTime, copy, lineno, type);
// Increase length when not enough place in the array
if (curEntry >= numEntries)
grow();
// Save the time spend logging the information in order to discard this time from the logged time.
// Especially needed when increasing the array or flushing the information.
loggingTime += rdtsc()-now;
}
void
TraceLogging::log(Type type, JSScript* script)
{
this->log(type, script->filename(), script->lineno);
}
void
TraceLogging::log(const char* log)
{
this->log(INFO, log, 0);
}
void
TraceLogging::log(Type type)
{
this->log(type, NULL, 0);
}
void
TraceLogging::flush()
{
// Open the logging file, when not opened yet.
if (out == NULL)
out = fopen(TRACE_LOG_DIR "tracelogging.log", "w");
// Print all log entries into the file
for (unsigned int i = 0; i < curEntry; i++) {
int written;
if (entries[i].type() == INFO) {
written = fprintf(out, "INFO,%s\n", entries[i].file());
} else {
if (entries[i].file() == NULL) {
written = fprintf(out, "%llu,%s\n",
(unsigned long long)entries[i].tick(),
type_name[entries[i].type()]);
} else {
written = fprintf(out, "%llu,%s,%s:%d\n",
(unsigned long long)entries[i].tick(),
type_name[entries[i].type()],
entries[i].file(),
entries[i].lineno());
}
}
// A logging file can only be 2GB of length (fwrite limit).
// When we exceed this limit, the writing will fail.
// In that case try creating a extra file to write the log entries.
if (written < 0) {
fclose(out);
if (fileno >= 9999)
exit(-1);
char filename[21 + sizeof(TRACE_LOG_DIR)];
sprintf (filename, TRACE_LOG_DIR "tracelogging-%d.log", ++fileno);
out = fopen(filename, "w");
i--; // Try to print message again
continue;
}
if (entries[i].file() != NULL) {
js_free(entries[i].file());
entries[i].file_ = NULL;
}
}
curEntry = 0;
}
TraceLogging*
TraceLogging::defaultLogger()
{
if (_defaultLogger == NULL) {
_defaultLogger = new TraceLogging();
atexit (releaseDefaultLogger);
}
return _defaultLogger;
}
void
TraceLogging::releaseDefaultLogger()
{
if (_defaultLogger != NULL) {
delete _defaultLogger;
_defaultLogger = NULL;
}
}
/* Helper functions for asm calls */
void
js::TraceLog(TraceLogging* logger, TraceLogging::Type type, JSScript* script)
{
logger->log(type, script);
}
void
js::TraceLog(TraceLogging* logger, const char* log)
{
logger->log(log);
}
void
js::TraceLog(TraceLogging* logger, TraceLogging::Type type)
{
logger->log(type);
}