unified_debug/example-c/unified_debug.cpp (115 lines of code) (raw):
/*
Copyright (c) 2012, 2016 Oracle and/or its affiliates. All rights
reserved.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; version 2 of
the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <node.h>
int uni_debug = 0;
int udeb_level = 0;
int udeb_initialized = 0;
int udeb_per_file = 0;
/* Undefine UNIFIED_DEBUG here so macros are not expanded
and uni_debug is not declared as an extern
*/
#undef UNIFIED_DEBUG
#include "unified_debug.h"
using namespace v8;
Persistent<Function> JSLoggerFunction;
/* Initialized to all zeros? */
unsigned char bit_index[UDEB_SOURCE_FILE_BITMASK_BYTES];
///// Internal Utility Functions
/* libc's basename(3) is not thread-safe, so we implement a version here.
This one is essentially a strlen() function that also remembers the final
path separator
*/
inline const char * udeb_basename(const char *path) {
const char * last_sep = 0;
if(path) {
const char * s = path;
last_sep = s;
for(; *s ; s++)
if(*s == '/')
last_sep = s;
if(last_sep > path && last_sep < s) // point just past the separator
last_sep += 1;
}
return last_sep;
}
// Bernstein hash
inline int udeb_hash(const char *name) {
const unsigned char *p;
unsigned int h = 5381;
for (p = (const unsigned char *) name ; *p != '\0' ; p++)
h = ((h << 5) + h) + *p;
h = h % UDEB_SOURCE_FILE_BITMASK_BITS;
return h;
}
// The bitmap hash table is used to flag per-file debugging.
inline int index_read(unsigned int bit_number) {
unsigned short byte = bit_number / 8;
unsigned char mask = 1 << ( bit_number % 8);
return bit_index[byte] & mask;
}
inline void index_set(unsigned int bit_number) {
unsigned short byte = bit_number / 8;
unsigned char mask = 1 << ( bit_number % 8);
bit_index[byte] |= mask;
}
inline void index_clear(unsigned int bit_number) {
unsigned short byte = bit_number / 8;
unsigned char mask = ((1 << ( bit_number % 8)) ^ 0xFF);
bit_index[byte] &= mask;
}
inline int log_level(const char *path) {
return index_read(udeb_hash(path)) ? UDEB_DETAIL : udeb_level;
}
////// The Logging API for C: udeb_print() and udeb_enter()
void udeb_enter(int level, const char *src_path, const char *fn, int ln) {
udeb_print(src_path, level, "Enter: %27s - line %d", fn, ln);
}
#define SEND_MESSAGES_TO_JAVASCRIPT 0
/* udeb_print() is used by macros in the public API
*/
void udeb_print(const char *src_path, int level, const char *fmt, ...) {
int sz = 0;
char message[UDEB_MSG_BUF];
const char * src_file = udeb_basename(src_path);
/* Construct the message */
va_list args;
va_start(args, fmt);
sz += snprintf(message, UDEB_MSG_BUF, "%s ", src_file);
sz += vsnprintf(message + sz, UDEB_MSG_BUF - sz, fmt, args);
va_end(args);
/* Some theory here:
This code works fine from the main JS thread, but not from uv worker threads.
We could register the uv_thread_self() id at setLogger() time, and then
compare it to the current uv_thread_self() when logging a message.
*/
if(udeb_initialized && log_level(src_file) >= level) {
#if SEND_MESSAGES_TO_JAVASCRIPT
HandleScope scope;
Handle<Value> jsArgs[3];
jsArgs[0] = Number::New(level);
jsArgs[1] = String::New(src_file);
jsArgs[2] = String::New(message, sz);
JSLoggerFunction->Call(Context::GetCurrent()->Global(), 3, jsArgs);
#else
sprintf(message + sz, "\n");
fputs(message, stderr);
#endif
}
return;
}
/************************* The JavaScript API ***********************
* setLevel(): JS tells C the global state and level.
* setLogger(): JS introduces itself to C and provides a logging function
* setFileLevel(): JS informs C to enable all messages from a particular file
***************/
Handle<Value> udeb_setLogger(const Arguments &args) {
HandleScope scope;
if(! udeb_initialized) {
Local<Function> f = Function::Cast(* (args[0]));
JSLoggerFunction = Persistent<Function>::New(f);
udeb_initialized = 1;
udeb_print("unified_debug.cpp", UDEB_DEBUG,
"unified_debug.cpp C++ unified_debug enabled");
}
return scope.Close(True());
}
Handle<Value> udeb_setLevel(const Arguments &args) {
HandleScope scope;
udeb_level = args[0]->Int32Value();
// C code cannot log below UDEB_INFO
uni_debug = (udeb_per_file || (udeb_level > UDEB_NOTICE)) ? 1 : 0;
return scope.Close(True());
}
Handle<Value> udeb_setFileLevel(const Arguments &args) {
HandleScope scope;
char filename[250];
args[0]->ToString()->WriteAscii(filename, 0, 250);
index_set(udeb_hash(udeb_basename(filename)));
uni_debug = udeb_per_file = 1;
return scope.Close(True());
}
#define DEFINE_JS_FUNCTION(TARGET, NAME, FN) \
TARGET->Set(String::NewSymbol(NAME), FunctionTemplate::New(FN)->GetFunction())
void udebug_initOnLoad(Handle<Object> target) {
DEFINE_JS_FUNCTION(target, "setLogger", udeb_setLogger);
DEFINE_JS_FUNCTION(target, "setLevel" , udeb_setLevel );
DEFINE_JS_FUNCTION(target, "setFileLevel", udeb_setFileLevel);
}