Skip to content

Convert rust_log.cpp to Rust, closes #8703 #8880

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion mk/rt.mk
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ RUNTIME_CXXS_$(1)_$(2) := \
rt/rust_upcall.cpp \
rt/rust_uv.cpp \
rt/rust_crate_map.cpp \
rt/rust_log.cpp \
rt/isaac/randport.cpp \
rt/miniz.cpp \
rt/memory_region.cpp \
Expand Down
263 changes: 248 additions & 15 deletions src/libstd/rt/logging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,159 @@
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use cast::transmute;
use either::*;
use libc;
use libc::{c_void, uintptr_t, c_char, exit, STDERR_FILENO};
use option::{Some, None};
use rt::util::dumb_println;
use str::StrSlice;
use str::raw::from_c_str;
use u32;
use unstable::raw::Closure;
use vec::ImmutableVector;


struct LogDirective {
name: ~str,
level: u32
}

// This is the Rust representation of the mod_entry struct in src/rt/rust_crate_map.h
struct ModEntry{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually a really-dangerous version of the mod_entry struct. Because this is C FFI, we can't assume that char* == ~str, so I think that this would be more appropriately written as

struct ModEntry {
  name: *c_char,
  log_level: *u32, // note the '*', this is important for the log level to actually get set.
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One of the main problems as-is is that if you drop the structure (if it didn't materialize through an & pointer), it would attempt to free name, leading to lots of pain

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does log_level have to be a pointer?

Shouldn't a normal value be enough, because its accessed and modified via a pointer to the struct?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what it's defined to be in the header file in C++. I think that we create a global in LLVM with some random name and then shove a pointer to the global in the mod_entry struct.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I overlooked that!

Should be fixed now

name: *c_char,
log_level: *mut u32
}

static MAX_LOG_DIRECTIVES: u32 = 255;
static MAX_LOG_LEVEL: u32 = 255;
static DEFAULT_LOG_LEVEL: u32 = 1;

fn iter_crate_map(map: *u8, f: &fn(*mut ModEntry)) {
unsafe {
let closure : Closure = transmute(f);
let code = transmute(closure.code);
let env = transmute(closure.env);
rust_iter_crate_map(transmute(map), iter_cb, code, env);
}

extern fn iter_cb(code: *c_void, env: *c_void, entry: *ModEntry){
unsafe {
let closure: Closure = Closure {
code: transmute(code),
env: transmute(env),
};
let closure: &fn(*ModEntry) = transmute(closure);
return closure(entry);
}
}
extern {
#[cfg(not(stage0))]
#[rust_stack]
fn rust_iter_crate_map(map: *c_void,
f: extern "C" fn(*c_void, *c_void, entry: *ModEntry),
code: *c_void,
data: *c_void);

#[cfg(stage0)]
#[rust_stack]
fn rust_iter_crate_map(map: *c_void,
f: *u8,
code: *c_void,
data: *c_void);
}
}

/// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=1")
/// and return a vector with log directives.
/// Valid log levels are 0-255, with the most likely ones being 0-3 (defined in std::).
fn parse_logging_spec(spec: ~str) -> ~[LogDirective]{
let mut dirs = ~[];
for s in spec.split_iter(',') {
let parts: ~[&str] = s.split_iter('=').collect();
let mut loglevel;
match parts.len() {
1 => loglevel = MAX_LOG_LEVEL,
2 => {
let num = u32::from_str(parts[1]);
match (num) {
Some(num) => {
if num < MAX_LOG_LEVEL {
loglevel = num;
} else {
loglevel = MAX_LOG_LEVEL;
}
}
_ => {
dumb_println(fmt!("warning: invalid logging spec \
'%s', ignoring it", s));
loop;
}
}
if loglevel > MAX_LOG_LEVEL { loglevel = MAX_LOG_LEVEL}
},
_ => {
dumb_println(fmt!("warning: invalid logging spec '%s',\
ignoring it", s));
loop;
}
}
let dir = LogDirective {name: parts[0].to_owned(), level: loglevel};
dirs.push(dir);
}
return dirs;
}

/// Set the log level of an entry in the crate map depending on the vector
/// of log directives
fn update_entry(dirs: &[LogDirective], entry: *mut ModEntry) -> u32 {
let mut new_lvl: u32 = DEFAULT_LOG_LEVEL;
let mut longest_match = 0;
unsafe {
for dir in dirs.iter() {
let name = from_c_str((*entry).name);
if name.starts_with(dir.name) && dir.name.len() > longest_match {
longest_match = dir.name.len();
new_lvl = dir.level;
}
}
*(*entry).log_level = new_lvl;
}
if longest_match > 0 { return 1; } else { return 0; }
}

#[fixed_stack_segment] #[inline(never)]
/// Set log level for every entry in crate_map according to the sepecification
/// in settings
fn update_log_settings(crate_map: *u8, settings: ~str) {
let mut dirs = ~[];
if settings.len() > 0 {
if settings == ~"::help" || settings == ~"?" {
dumb_println("\nCrate log map:\n");
do iter_crate_map(crate_map) |entry: *mut ModEntry| {
unsafe {
dumb_println(" "+from_c_str((*entry).name));
}
}
unsafe {
exit(1);
}
}
dirs = parse_logging_spec(settings);
}

let mut n_matches: u32 = 0;
do iter_crate_map(crate_map) |entry: *mut ModEntry| {
let m = update_entry(dirs, entry);
n_matches += m;
}

if n_matches < (dirs.len() as u32) {
dumb_println(fmt!("warning: got %u RUST_LOG specs but only matched %u of them.\n\
You may have mistyped a RUST_LOG spec.\n\
Use RUST_LOG=::help to see the list of crates and modules.\n",
dirs.len() as uint, n_matches as uint));
}
}

pub trait Logger {
fn log(&mut self, msg: Either<~str, &'static str>);
Expand Down Expand Up @@ -47,34 +196,26 @@ impl Logger for StdErrLogger {
};

fn print(s: &str) {
let dbg = ::libc::STDERR_FILENO as ::io::fd_t;
let dbg = STDERR_FILENO as ::io::fd_t;
dbg.write_str(s);
dbg.write_str("\n");
dbg.flush();
}
}
}

/// Configure logging by traversing the crate map and setting the
/// per-module global logging flags based on the logging spec
#[fixed_stack_segment] #[inline(never)]
pub fn init(crate_map: *u8) {
use c_str::ToCStr;
use os;
use ptr;
use option::{Some, None};

let log_spec = os::getenv("RUST_LOG");
match log_spec {
Some(spec) => {
do spec.with_c_str |buf| {
unsafe { rust_update_log_settings(crate_map, buf) }
}
update_log_settings(crate_map, spec);
}
None => {
unsafe {
rust_update_log_settings(crate_map, ptr::null());
}
update_log_settings(crate_map, ~"");
}
}
}
Expand All @@ -89,9 +230,101 @@ pub fn console_off() { unsafe { rust_log_console_off() } }
fn should_log_console() -> bool { unsafe { rust_should_log_console() != 0 } }

extern {
fn rust_update_log_settings(crate_map: *u8, settings: *libc::c_char);
fn rust_log_console_on();
fn rust_log_console_off();
fn rust_should_log_console() -> libc::uintptr_t;
fn rust_should_log_console() -> uintptr_t;
}

// Tests for parse_logging_spec()
#[test]
fn parse_logging_spec_valid() {
let dirs: ~[LogDirective] = parse_logging_spec(~"crate1::mod1=1,crate1::mod2,crate2=4");
assert_eq!(dirs.len(), 3);
assert!(dirs[0].name == ~"crate1::mod1");
assert_eq!(dirs[0].level, 1);

assert!(dirs[1].name == ~"crate1::mod2");
assert_eq!(dirs[1].level, MAX_LOG_LEVEL);

assert!(dirs[2].name == ~"crate2");
assert_eq!(dirs[2].level, 4);
}

#[test]
fn parse_logging_spec_invalid_crate() {
// test parse_logging_spec with multiple = in specification
let dirs: ~[LogDirective] = parse_logging_spec(~"crate1::mod1=1=2,crate2=4");
assert_eq!(dirs.len(), 1);
assert!(dirs[0].name == ~"crate2");
assert_eq!(dirs[0].level, 4);
}

#[test]
fn parse_logging_spec_invalid_log_level() {
// test parse_logging_spec with 'noNumber' as log level
let dirs: ~[LogDirective] = parse_logging_spec(~"crate1::mod1=noNumber,crate2=4");
assert_eq!(dirs.len(), 1);
assert!(dirs[0].name == ~"crate2");
assert_eq!(dirs[0].level, 4);
}

// Tests for update_entry
#[test]
fn update_entry_match_full_path() {
use c_str::ToCStr;
let dirs = ~[LogDirective {name: ~"crate1::mod1", level: 2 },
LogDirective {name: ~"crate2", level: 3}];
unsafe {
do "crate1::mod1".to_c_str().with_ref |ptr| {
let entry= &ModEntry {name: ptr, log_level: &mut 0};
let m = update_entry(dirs, transmute(entry));
assert!(*entry.log_level == 2);
assert!(m == 1);
}
}
}

#[test]
fn update_entry_no_match() {
use c_str::ToCStr;
let dirs = ~[LogDirective {name: ~"crate1::mod1", level: 2 },
LogDirective {name: ~"crate2", level: 3}];
unsafe {
do "crate3::mod1".to_c_str().with_ref |ptr| {
let entry= &ModEntry {name: ptr, log_level: &mut 0};
let m = update_entry(dirs, transmute(entry));
assert!(*entry.log_level == DEFAULT_LOG_LEVEL);
assert!(m == 0);
}
}
}

#[test]
fn update_entry_match_beginning() {
use c_str::ToCStr;
let dirs = ~[LogDirective {name: ~"crate1::mod1", level: 2 },
LogDirective {name: ~"crate2", level: 3}];
unsafe {
do "crate2::mod1".to_c_str().with_ref |ptr| {
let entry= &ModEntry {name: ptr, log_level: &mut 0};
let m = update_entry(dirs, transmute(entry));
assert!(*entry.log_level == 3);
assert!(m == 1);
}
}
}

#[test]
fn update_entry_match_beginning_longest_match() {
use c_str::ToCStr;
let dirs = ~[LogDirective {name: ~"crate1::mod1", level: 2 },
LogDirective {name: ~"crate2", level: 3}, LogDirective {name: ~"crate2::mod", level: 4}];
unsafe {
do "crate2::mod1".to_c_str().with_ref |ptr| {
let entry = &ModEntry {name: ptr, log_level: &mut 0};
let m = update_entry(dirs, transmute(entry));
assert!(*entry.log_level == 4);
assert!(m == 1);
}
}
}
38 changes: 27 additions & 11 deletions src/rt/rust_crate_map.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
Expand All @@ -12,36 +12,52 @@
#include <set>

void iter_module_map(const mod_entry* map,
void (*fn)(const mod_entry* entry, void *cookie),
void *cookie) {
void (*fn)(void* fptr, void* env, const mod_entry *entry),
void* fptr,
void* env
) {
for (const mod_entry* cur = map; cur->name; cur++) {
fn(cur, cookie);
fn(fptr, env, cur);
}
}

void iter_crate_map(const cratemap* map,
void (*fn)(const mod_entry* map, void *cookie),
void *cookie,
void (*fn)(void* fptr, void* env, const mod_entry *entry),
void *fptr,
void *env,
std::set<const cratemap*>& visited) {
if (visited.find(map) == visited.end()) {
// Mark this crate visited
visited.insert(map);
// First iterate this crate
iter_module_map(map->entries(), fn, cookie);
iter_module_map(map->entries(), fn, fptr, env);
// Then recurse on linked crates
for (cratemap::iterator i = map->begin(),
e = map->end(); i != e; ++i) {
iter_crate_map(*i, fn, cookie, visited);
iter_crate_map(*i, fn, fptr, env, visited);
}
}
}

void iter_crate_map(const cratemap* map,
void (*fn)(const mod_entry* map, void *cookie),
void *cookie) {
void (*fn)(void* fptr, void* env, const mod_entry *entry),
void *fptr,
void *env
) {
std::set<const cratemap*> visited;
iter_crate_map(map, fn, cookie, visited);
iter_crate_map(map, fn, fptr, env, visited);
}

extern "C" CDECL void
rust_iter_crate_map(const cratemap* map,
void (*fn)(void* fptr, void* env, const mod_entry *entry),
void *fptr,
void *env
) {
return iter_crate_map(map, fn, fptr, env);
}


//
// Local Variables:
// mode: C++
Expand Down
Loading