77// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
88// option. This file may not be copied, modified, or distributed
99// except according to those terms.
10-
10+ use cast :: transmute ;
1111use either:: * ;
12- use libc;
12+ use libc:: { c_void, uintptr_t, c_char, exit, STDERR_FILENO } ;
13+ use option:: { Some , None } ;
14+ use rt:: util:: dumb_println;
1315use str:: StrSlice ;
16+ use str:: raw:: from_c_str;
17+ use u32;
18+ use unstable:: raw:: Closure ;
19+ use vec:: ImmutableVector ;
20+
21+
22+ struct LogDirective {
23+ name : ~str ,
24+ level : u32
25+ }
26+
27+ // This is the Rust representation of the mod_entry struct in src/rt/rust_crate_map.h
28+ struct ModEntry {
29+ name : * c_char ,
30+ log_level : * mut u32
31+ }
32+
33+ static MAX_LOG_DIRECTIVES : u32 = 255 ;
34+ static MAX_LOG_LEVEL : u32 = 255 ;
35+ static DEFAULT_LOG_LEVEL : u32 = 1 ;
36+
37+ fn iter_crate_map ( map : * u8 , f : & fn ( * mut ModEntry ) ) {
38+ unsafe {
39+ let closure : Closure = transmute ( f) ;
40+ let code = transmute ( closure. code ) ;
41+ let env = transmute ( closure. env ) ;
42+ rust_iter_crate_map ( transmute ( map) , iter_cb, code, env) ;
43+ }
44+
45+ extern fn iter_cb ( code : * c_void , env : * c_void , entry : * ModEntry ) {
46+ unsafe {
47+ let closure: Closure = Closure {
48+ code : transmute ( code) ,
49+ env : transmute ( env) ,
50+ } ;
51+ let closure: & fn ( * ModEntry ) = transmute ( closure) ;
52+ return closure ( entry) ;
53+ }
54+ }
55+ extern {
56+ #[ cfg( not( stage0) ) ]
57+ #[ rust_stack]
58+ fn rust_iter_crate_map ( map : * c_void ,
59+ f : extern "C" fn ( * c_void , * c_void , entry : * ModEntry ) ,
60+ code : * c_void ,
61+ data : * c_void ) ;
62+
63+ #[ cfg( stage0) ]
64+ #[ rust_stack]
65+ fn rust_iter_crate_map ( map : * c_void ,
66+ f : * u8 ,
67+ code : * c_void ,
68+ data : * c_void ) ;
69+ }
70+ }
71+
72+ /// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=1")
73+ /// and return a vector with log directives.
74+ /// Valid log levels are 0-255, with the most likely ones being 0-3 (defined in std::).
75+ fn parse_logging_spec ( spec : ~str ) -> ~[ LogDirective ] {
76+ let mut dirs = ~[ ] ;
77+ for s in spec. split_iter ( ',' ) {
78+ let parts: ~[ & str ] = s. split_iter ( '=' ) . collect ( ) ;
79+ let mut loglevel;
80+ match parts. len ( ) {
81+ 1 => loglevel = MAX_LOG_LEVEL ,
82+ 2 => {
83+ let num = u32:: from_str ( parts[ 1 ] ) ;
84+ match ( num) {
85+ Some ( num) => {
86+ if num < MAX_LOG_LEVEL {
87+ loglevel = num;
88+ } else {
89+ loglevel = MAX_LOG_LEVEL ;
90+ }
91+ }
92+ _ => {
93+ dumb_println ( fmt ! ( "warning: invalid logging spec \
94+ '%s', ignoring it", s) ) ;
95+ loop ;
96+ }
97+ }
98+ if loglevel > MAX_LOG_LEVEL { loglevel = MAX_LOG_LEVEL }
99+ } ,
100+ _ => {
101+ dumb_println ( fmt ! ( "warning: invalid logging spec '%s',\
102+ ignoring it", s) ) ;
103+ loop ;
104+ }
105+ }
106+ let dir = LogDirective { name : parts[ 0 ] . to_owned ( ) , level : loglevel} ;
107+ dirs. push ( dir) ;
108+ }
109+ return dirs;
110+ }
111+
112+ /// Set the log level of an entry in the crate map depending on the vector
113+ /// of log directives
114+ fn update_entry ( dirs : & [ LogDirective ] , entry : * mut ModEntry ) -> u32 {
115+ let mut new_lvl: u32 = DEFAULT_LOG_LEVEL ;
116+ let mut longest_match = 0 ;
117+ unsafe {
118+ for dir in dirs. iter ( ) {
119+ let name = from_c_str ( ( * entry) . name ) ;
120+ if name. starts_with ( dir. name ) && dir. name . len ( ) > longest_match {
121+ longest_match = dir. name . len ( ) ;
122+ new_lvl = dir. level ;
123+ }
124+ }
125+ * ( * entry) . log_level = new_lvl;
126+ }
127+ if longest_match > 0 { return 1 ; } else { return 0 ; }
128+ }
129+
130+ #[ fixed_stack_segment] #[ inline( never) ]
131+ /// Set log level for every entry in crate_map according to the sepecification
132+ /// in settings
133+ fn update_log_settings( crate_map : * u8 , settings : ~str ) {
134+ let mut dirs = ~[ ] ;
135+ if settings. len ( ) > 0 {
136+ if settings == ~":: help" || settings == ~"?" {
137+ dumb_println ( "\n Crate log map:\n " ) ;
138+ do iter_crate_map ( crate_map) |entry: * mut ModEntry | {
139+ unsafe {
140+ dumb_println ( " " +from_c_str ( ( * entry) . name ) ) ;
141+ }
142+ }
143+ unsafe {
144+ exit ( 1 ) ;
145+ }
146+ }
147+ dirs = parse_logging_spec ( settings) ;
148+ }
149+
150+ let mut n_matches: u32 = 0 ;
151+ do iter_crate_map ( crate_map) |entry: * mut ModEntry | {
152+ let m = update_entry ( dirs, entry) ;
153+ n_matches += m;
154+ }
155+
156+ if n_matches < ( dirs. len ( ) as u32 ) {
157+ dumb_println ( fmt ! ( "warning: got %u RUST_LOG specs but only matched %u of them.\n \
158+ You may have mistyped a RUST_LOG spec.\n \
159+ Use RUST_LOG=::help to see the list of crates and modules.\n ",
160+ dirs. len( ) as uint, n_matches as uint) ) ;
161+ }
162+ }
14163
15164pub trait Logger {
16165 fn log ( & mut self , msg : Either < ~str , & ' static str > ) ;
@@ -47,34 +196,26 @@ impl Logger for StdErrLogger {
47196 } ;
48197
49198 fn print ( s : & str ) {
50- let dbg = :: libc :: STDERR_FILENO as :: io:: fd_t ;
199+ let dbg = STDERR_FILENO as :: io:: fd_t ;
51200 dbg. write_str ( s) ;
52201 dbg. write_str ( "\n " ) ;
53202 dbg. flush ( ) ;
54203 }
55204 }
56205}
57-
58206/// Configure logging by traversing the crate map and setting the
59207/// per-module global logging flags based on the logging spec
60208#[ fixed_stack_segment] #[ inline( never) ]
61209pub fn init ( crate_map : * u8 ) {
62- use c_str:: ToCStr ;
63210 use os;
64- use ptr;
65- use option:: { Some , None } ;
66211
67212 let log_spec = os:: getenv ( "RUST_LOG" ) ;
68213 match log_spec {
69214 Some ( spec) => {
70- do spec. with_c_str |buf| {
71- unsafe { rust_update_log_settings ( crate_map, buf) }
72- }
215+ update_log_settings ( crate_map, spec) ;
73216 }
74217 None => {
75- unsafe {
76- rust_update_log_settings ( crate_map, ptr:: null ( ) ) ;
77- }
218+ update_log_settings ( crate_map, ~"") ;
78219 }
79220 }
80221}
@@ -89,9 +230,101 @@ pub fn console_off() { unsafe { rust_log_console_off() } }
89230fn should_log_console ( ) -> bool { unsafe { rust_should_log_console ( ) != 0 } }
90231
91232extern {
92- fn rust_update_log_settings ( crate_map : * u8 , settings : * libc:: c_char ) ;
93233 fn rust_log_console_on ( ) ;
94234 fn rust_log_console_off ( ) ;
95- fn rust_should_log_console ( ) -> libc:: uintptr_t ;
235+ fn rust_should_log_console ( ) -> uintptr_t ;
236+ }
237+
238+ // Tests for parse_logging_spec()
239+ #[ test]
240+ fn parse_logging_spec_valid ( ) {
241+ let dirs: ~[ LogDirective ] = parse_logging_spec ( ~"crate1:: mod1=1 , crate1:: mod2, crate2=4 ") ;
242+ assert_eq ! ( dirs. len( ) , 3 ) ;
243+ assert ! ( dirs[ 0 ] . name == ~"crate1:: mod1");
244+ assert_eq!(dirs[0].level, 1);
245+
246+ assert!(dirs[1].name == ~" crate1:: mod2");
247+ assert_eq!(dirs[1].level, MAX_LOG_LEVEL);
248+
249+ assert!(dirs[2].name == ~" crate2");
250+ assert_eq!(dirs[2].level, 4);
251+ }
252+
253+ #[test]
254+ fn parse_logging_spec_invalid_crate() {
255+ // test parse_logging_spec with multiple = in specification
256+ let dirs: ~[LogDirective] = parse_logging_spec(~" crate1:: mod1=1 =2 , crate2=4 ");
257+ assert_eq!(dirs.len(), 1);
258+ assert!(dirs[0].name == ~" crate2");
259+ assert_eq!(dirs[0].level, 4);
260+ }
261+
262+ #[test]
263+ fn parse_logging_spec_invalid_log_level() {
264+ // test parse_logging_spec with 'noNumber' as log level
265+ let dirs: ~[LogDirective] = parse_logging_spec(~" crate1:: mod1=noNumber, crate2=4 ");
266+ assert_eq!(dirs.len(), 1);
267+ assert!(dirs[0].name == ~" crate2");
268+ assert_eq!(dirs[0].level, 4);
269+ }
270+
271+ // Tests for update_entry
272+ #[test]
273+ fn update_entry_match_full_path() {
274+ use c_str::ToCStr;
275+ let dirs = ~[LogDirective {name: ~" crate1:: mod1", level: 2 },
276+ LogDirective {name: ~" crate2", level: 3}];
277+ unsafe {
278+ do " crate1:: mod1".to_c_str().with_ref |ptr| {
279+ let entry= &ModEntry {name: ptr, log_level: &mut 0};
280+ let m = update_entry(dirs, transmute(entry));
281+ assert!(*entry.log_level == 2);
282+ assert!(m == 1);
283+ }
284+ }
96285}
97286
287+ #[test]
288+ fn update_entry_no_match() {
289+ use c_str::ToCStr;
290+ let dirs = ~[LogDirective {name: ~" crate1:: mod1", level: 2 },
291+ LogDirective {name: ~" crate2", level: 3}];
292+ unsafe {
293+ do " crate3:: mod1".to_c_str().with_ref |ptr| {
294+ let entry= &ModEntry {name: ptr, log_level: &mut 0};
295+ let m = update_entry(dirs, transmute(entry));
296+ assert!(*entry.log_level == DEFAULT_LOG_LEVEL);
297+ assert!(m == 0);
298+ }
299+ }
300+ }
301+
302+ #[test]
303+ fn update_entry_match_beginning() {
304+ use c_str::ToCStr;
305+ let dirs = ~[LogDirective {name: ~" crate1:: mod1", level: 2 },
306+ LogDirective {name: ~" crate2", level: 3}];
307+ unsafe {
308+ do " crate2:: mod1".to_c_str().with_ref |ptr| {
309+ let entry= &ModEntry {name: ptr, log_level: &mut 0};
310+ let m = update_entry(dirs, transmute(entry));
311+ assert!(*entry.log_level == 3);
312+ assert!(m == 1);
313+ }
314+ }
315+ }
316+
317+ #[test]
318+ fn update_entry_match_beginning_longest_match() {
319+ use c_str::ToCStr;
320+ let dirs = ~[LogDirective {name: ~" crate1:: mod1", level: 2 },
321+ LogDirective {name: ~" crate2", level: 3}, LogDirective {name: ~" crate2:: mod ", level: 4}];
322+ unsafe {
323+ do " crate2:: mod1" . to_c_str( ) . with_ref |ptr| {
324+ let entry = & ModEntry { name: ptr, log_level: & mut 0 } ;
325+ let m = update_entry( dirs, transmute( entry) ) ;
326+ assert!( * entry. log_level == 4 ) ;
327+ assert!( m == 1 ) ;
328+ }
329+ }
330+ }
0 commit comments