432978367e
Move CodeObserver registration to the OS abstraction. Move Pprof logic to the OS abstraction. Remove pprof from VM tests because now pprof logic is in the VM itself. Add DISALLOW and AllStatic to Code Observers where appropriate. Previous review: https://codereview.chromium.org/11572032/ BUG=7321 Review URL: https://codereview.chromium.org//11660011 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@16462 260f80e4-7a28-3924-810f-c04153c831b5
415 lines
11 KiB
C++
415 lines
11 KiB
C++
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#include "vm/os.h"
|
|
|
|
#include <android/log.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <malloc.h>
|
|
#include <time.h>
|
|
#include <sys/resource.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include "platform/utils.h"
|
|
#include "vm/code_observers.h"
|
|
#include "vm/dart.h"
|
|
#include "vm/debuginfo.h"
|
|
#include "vm/isolate.h"
|
|
#include "vm/zone.h"
|
|
|
|
|
|
namespace dart {
|
|
|
|
// Android CodeObservers.
|
|
|
|
DEFINE_FLAG(bool, generate_gdb_symbols, false,
|
|
"Generate symbols of generated dart functions for debugging with GDB");
|
|
DEFINE_FLAG(bool, generate_perf_events_symbols, false,
|
|
"Generate events symbols for profiling with perf");
|
|
DEFINE_FLAG(charp, generate_pprof_symbols, false,
|
|
"Generate events symbols for profiling with pprof");
|
|
|
|
class PerfCodeObserver : public CodeObserver {
|
|
public:
|
|
PerfCodeObserver() {
|
|
Dart_FileOpenCallback file_open = Isolate::file_open_callback();
|
|
if (file_open == NULL) {
|
|
return;
|
|
}
|
|
const char* format = "/tmp/perf-%ld.map";
|
|
intptr_t pid = getpid();
|
|
intptr_t len = OS::SNPrint(NULL, 0, format, pid);
|
|
char* filename = new char[len + 1];
|
|
OS::SNPrint(filename, len + 1, format, pid);
|
|
out_file_ = (*file_open)(filename);
|
|
}
|
|
|
|
~PerfCodeObserver() {
|
|
Dart_FileCloseCallback file_close = Isolate::file_close_callback();
|
|
if (file_close == NULL) {
|
|
return;
|
|
}
|
|
ASSERT(out_file_ != NULL);
|
|
(*file_close)(out_file_);
|
|
}
|
|
|
|
virtual bool IsActive() const {
|
|
return FLAG_generate_perf_events_symbols;
|
|
}
|
|
|
|
virtual void Notify(const char* name,
|
|
uword base,
|
|
uword prologue_offset,
|
|
uword size,
|
|
bool optimized) {
|
|
Dart_FileWriteCallback file_write = Isolate::file_write_callback();
|
|
ASSERT(file_write != NULL);
|
|
const char* format = "%"Px" %"Px" %s%s\n";
|
|
const char* marker = optimized ? "*" : "";
|
|
intptr_t len = OS::SNPrint(NULL, 0, format, base, size, marker, name);
|
|
char* buffer = Isolate::Current()->current_zone()->Alloc<char>(len + 1);
|
|
OS::SNPrint(buffer, len + 1, format, base, size, marker, name);
|
|
ASSERT(out_file_ != NULL);
|
|
(*file_write)(buffer, len, out_file_);
|
|
}
|
|
|
|
private:
|
|
void* out_file_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(PerfCodeObserver);
|
|
};
|
|
|
|
class PprofCodeObserver : public CodeObserver {
|
|
public:
|
|
PprofCodeObserver() {
|
|
pprof_symbol_generator_ = DebugInfo::NewGenerator();
|
|
}
|
|
|
|
~PprofCodeObserver() {
|
|
Dart_FileOpenCallback file_open = Isolate::file_open_callback();
|
|
if (file_open == NULL) {
|
|
return;
|
|
}
|
|
Dart_FileCloseCallback file_close = Isolate::file_close_callback();
|
|
if (file_close == NULL) {
|
|
return;
|
|
}
|
|
Dart_FileWriteCallback file_write = Isolate::file_write_callback();
|
|
if (file_write == NULL) {
|
|
return;
|
|
}
|
|
if (FLAG_generate_pprof_symbols == NULL) {
|
|
return;
|
|
}
|
|
const char* filename = FLAG_generate_pprof_symbols;
|
|
void* out_file = (*file_open)(filename);
|
|
ASSERT(out_file != NULL);
|
|
DebugInfo::ByteBuffer* debug_region = new DebugInfo::ByteBuffer();
|
|
ASSERT(debug_region != NULL);
|
|
pprof_symbol_generator_->WriteToMemory(debug_region);
|
|
int buffer_size = debug_region->size();
|
|
void* buffer = debug_region->data();
|
|
delete debug_region;
|
|
if (buffer_size > 0) {
|
|
ASSERT(buffer != NULL);
|
|
(*file_write)(buffer, buffer_size, out_file);
|
|
}
|
|
(*file_close)(out_file);
|
|
DebugInfo::UnregisterAllSections();
|
|
}
|
|
|
|
virtual bool IsActive() const {
|
|
return FLAG_generate_pprof_symbols != NULL;
|
|
}
|
|
|
|
virtual void Notify(const char* name,
|
|
uword base,
|
|
uword prologue_offset,
|
|
uword size,
|
|
bool optimized) {
|
|
ASSERT(pprof_symbol_generator_ != NULL);
|
|
pprof_symbol_generator_->AddCode(base, size);
|
|
pprof_symbol_generator_->AddCodeRegion(name, base, size);
|
|
}
|
|
|
|
private:
|
|
DebugInfo* pprof_symbol_generator_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(PprofCodeObserver);
|
|
};
|
|
|
|
class GdbCodeObserver : public CodeObserver {
|
|
public:
|
|
GdbCodeObserver() { }
|
|
|
|
virtual bool IsActive() const {
|
|
return FLAG_generate_gdb_symbols;
|
|
}
|
|
|
|
virtual void Notify(const char* name,
|
|
uword base,
|
|
uword prologue_offset,
|
|
uword size,
|
|
bool optimized) {
|
|
if (prologue_offset > 0) {
|
|
// In order to ensure that gdb sees the first instruction of a function
|
|
// as the prologue sequence we register two symbols for the cases when
|
|
// the prologue sequence is not the first instruction:
|
|
// <name>_entry is used for code preceding the prologue sequence.
|
|
// <name> for rest of the code (first instruction is prologue sequence).
|
|
const char* kFormat = "%s_%s";
|
|
intptr_t len = OS::SNPrint(NULL, 0, kFormat, name, "entry");
|
|
char* pname = Isolate::Current()->current_zone()->Alloc<char>(len + 1);
|
|
OS::SNPrint(pname, (len + 1), kFormat, name, "entry");
|
|
DebugInfo::RegisterSection(pname, base, size);
|
|
DebugInfo::RegisterSection(name,
|
|
(base + prologue_offset),
|
|
(size - prologue_offset));
|
|
} else {
|
|
DebugInfo::RegisterSection(name, base, size);
|
|
}
|
|
}
|
|
|
|
private:
|
|
DISALLOW_COPY_AND_ASSIGN(GdbCodeObserver);
|
|
};
|
|
|
|
|
|
static bool LocalTime(int64_t seconds_since_epoch, tm* tm_result) {
|
|
time_t seconds = static_cast<time_t>(seconds_since_epoch);
|
|
if (seconds != seconds_since_epoch) return false;
|
|
struct tm* error_code = localtime_r(&seconds, tm_result);
|
|
return error_code != NULL;
|
|
}
|
|
|
|
|
|
const char* OS::GetTimeZoneName(int64_t seconds_since_epoch) {
|
|
tm decomposed;
|
|
bool succeeded = LocalTime(seconds_since_epoch, &decomposed);
|
|
// If unsuccessful, return an empty string like V8 does.
|
|
return (succeeded && (decomposed.tm_zone != NULL)) ? decomposed.tm_zone : "";
|
|
}
|
|
|
|
|
|
int OS::GetTimeZoneOffsetInSeconds(int64_t seconds_since_epoch) {
|
|
tm decomposed;
|
|
bool succeeded = LocalTime(seconds_since_epoch, &decomposed);
|
|
// Even if the offset was 24 hours it would still easily fit into 32 bits.
|
|
// If unsuccessful, return zero like V8 does.
|
|
return succeeded ? static_cast<int>(decomposed.tm_gmtoff) : 0;
|
|
}
|
|
|
|
|
|
int OS::GetLocalTimeZoneAdjustmentInSeconds() {
|
|
// TODO(floitsch): avoid excessive calls to tzset?
|
|
tzset();
|
|
// Even if the offset was 24 hours it would still easily fit into 32 bits.
|
|
// Note that Unix and Dart disagree on the sign.
|
|
return static_cast<int>(-timezone);
|
|
}
|
|
|
|
|
|
int64_t OS::GetCurrentTimeMillis() {
|
|
return GetCurrentTimeMicros() / 1000;
|
|
}
|
|
|
|
|
|
int64_t OS::GetCurrentTimeMicros() {
|
|
// gettimeofday has microsecond resolution.
|
|
struct timeval tv;
|
|
if (gettimeofday(&tv, NULL) < 0) {
|
|
UNREACHABLE();
|
|
return 0;
|
|
}
|
|
return (static_cast<int64_t>(tv.tv_sec) * 1000000) + tv.tv_usec;
|
|
}
|
|
|
|
|
|
void* OS::AlignedAllocate(intptr_t size, intptr_t alignment) {
|
|
const int kMinimumAlignment = 16;
|
|
ASSERT(Utils::IsPowerOfTwo(alignment));
|
|
ASSERT(alignment >= kMinimumAlignment);
|
|
void* p = memalign(alignment, size);
|
|
if (p == NULL) {
|
|
UNREACHABLE();
|
|
}
|
|
return p;
|
|
}
|
|
|
|
|
|
void OS::AlignedFree(void* ptr) {
|
|
free(ptr);
|
|
}
|
|
|
|
|
|
// TODO(5411554): May need to hoist these architecture dependent code
|
|
// into a architecture specific file e.g: os_ia32_linux.cc
|
|
word OS::ActivationFrameAlignment() {
|
|
#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64)
|
|
const int kMinimumAlignment = 16;
|
|
#elif defined(TARGET_ARCH_ARM)
|
|
const int kMinimumAlignment = 8;
|
|
#else
|
|
#error Unsupported architecture.
|
|
#endif
|
|
word alignment = kMinimumAlignment;
|
|
// TODO(5411554): Allow overriding default stack alignment for
|
|
// testing purposes.
|
|
// Flags::DebugIsInt("stackalign", &alignment);
|
|
ASSERT(Utils::IsPowerOfTwo(alignment));
|
|
ASSERT(alignment >= kMinimumAlignment);
|
|
return alignment;
|
|
}
|
|
|
|
|
|
word OS::PreferredCodeAlignment() {
|
|
#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64)
|
|
const int kMinimumAlignment = 16;
|
|
#elif defined(TARGET_ARCH_ARM)
|
|
const int kMinimumAlignment = 16;
|
|
#else
|
|
#error Unsupported architecture.
|
|
#endif
|
|
word alignment = kMinimumAlignment;
|
|
// TODO(5411554): Allow overriding default code alignment for
|
|
// testing purposes.
|
|
// Flags::DebugIsInt("codealign", &alignment);
|
|
ASSERT(Utils::IsPowerOfTwo(alignment));
|
|
ASSERT(alignment >= kMinimumAlignment);
|
|
ASSERT(alignment <= OS::kMaxPreferredCodeAlignment);
|
|
return alignment;
|
|
}
|
|
|
|
|
|
uword OS::GetStackSizeLimit() {
|
|
struct rlimit stack_limit;
|
|
int retval = getrlimit(RLIMIT_STACK, &stack_limit);
|
|
ASSERT(retval == 0);
|
|
if (stack_limit.rlim_cur > INT_MAX) {
|
|
retval = INT_MAX;
|
|
} else {
|
|
retval = stack_limit.rlim_cur;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
|
|
int OS::NumberOfAvailableProcessors() {
|
|
return sysconf(_SC_NPROCESSORS_ONLN);
|
|
}
|
|
|
|
|
|
void OS::Sleep(int64_t millis) {
|
|
// TODO(5411554): For now just use usleep we may have to revisit this.
|
|
usleep(millis * 1000);
|
|
}
|
|
|
|
|
|
void OS::Print(const char* format, ...) {
|
|
va_list args;
|
|
va_start(args, format);
|
|
VFPrint(stdout, format, args);
|
|
// Forward to the Android log for remote access.
|
|
__android_log_vprint(ANDROID_LOG_INFO, "DartVM", format, args);
|
|
va_end(args);
|
|
}
|
|
|
|
|
|
void OS::VFPrint(FILE* stream, const char* format, va_list args) {
|
|
vfprintf(stream, format, args);
|
|
fflush(stream);
|
|
}
|
|
|
|
|
|
int OS::SNPrint(char* str, size_t size, const char* format, ...) {
|
|
va_list args;
|
|
va_start(args, format);
|
|
int retval = VSNPrint(str, size, format, args);
|
|
va_end(args);
|
|
return retval;
|
|
}
|
|
|
|
|
|
int OS::VSNPrint(char* str, size_t size, const char* format, va_list args) {
|
|
int retval = vsnprintf(str, size, format, args);
|
|
if (retval < 0) {
|
|
FATAL1("Fatal error in OS::VSNPrint with format '%s'", format);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
|
|
bool OS::StringToInt64(const char* str, int64_t* value) {
|
|
ASSERT(str != NULL && strlen(str) > 0 && value != NULL);
|
|
int32_t base = 10;
|
|
char* endptr;
|
|
int i = 0;
|
|
if (str[0] == '-') {
|
|
i = 1;
|
|
}
|
|
if ((str[i] == '0') &&
|
|
(str[i + 1] == 'x' || str[i + 1] == 'X') &&
|
|
(str[i + 2] != '\0')) {
|
|
base = 16;
|
|
}
|
|
errno = 0;
|
|
*value = strtoll(str, &endptr, base);
|
|
return ((errno == 0) && (endptr != str) && (*endptr == 0));
|
|
}
|
|
|
|
|
|
void OS::RegisterCodeObservers() {
|
|
if (FLAG_generate_perf_events_symbols) {
|
|
CodeObservers::Register(new PerfCodeObserver);
|
|
}
|
|
if (FLAG_generate_gdb_symbols) {
|
|
CodeObservers::Register(new GdbCodeObserver);
|
|
}
|
|
if (FLAG_generate_pprof_symbols != NULL) {
|
|
CodeObservers::Register(new PprofCodeObserver);
|
|
}
|
|
#if defined(DART_VTUNE_SUPPORT)
|
|
Register(new VTuneCodeObserver);
|
|
#endif
|
|
}
|
|
|
|
|
|
void OS::PrintErr(const char* format, ...) {
|
|
va_list args;
|
|
va_start(args, format);
|
|
VFPrint(stderr, format, args);
|
|
// Forward to the Android log for remote access.
|
|
__android_log_vprint(ANDROID_LOG_ERROR, "DartVM", format, args);
|
|
va_end(args);
|
|
}
|
|
|
|
|
|
void OS::InitOnce() {
|
|
// TODO(5411554): For now we check that initonce is called only once,
|
|
// Once there is more formal mechanism to call InitOnce we can move
|
|
// this check there.
|
|
static bool init_once_called = false;
|
|
ASSERT(init_once_called == false);
|
|
init_once_called = true;
|
|
}
|
|
|
|
|
|
void OS::Shutdown() {
|
|
}
|
|
|
|
|
|
void OS::Abort() {
|
|
abort();
|
|
}
|
|
|
|
|
|
void OS::Exit(int code) {
|
|
exit(code);
|
|
}
|
|
|
|
} // namespace dart
|