7
7
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8
8
// option. This file may not be copied, modified, or distributed
9
9
// except according to those terms.
10
-
10
+ use cast :: transmute ;
11
11
use 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;
13
15
use 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
+ }
14
163
15
164
pub trait Logger {
16
165
fn log ( & mut self , msg : Either < ~str , & ' static str > ) ;
@@ -47,34 +196,26 @@ impl Logger for StdErrLogger {
47
196
} ;
48
197
49
198
fn print ( s : & str ) {
50
- let dbg = :: libc :: STDERR_FILENO as :: io:: fd_t ;
199
+ let dbg = STDERR_FILENO as :: io:: fd_t ;
51
200
dbg. write_str ( s) ;
52
201
dbg. write_str ( "\n " ) ;
53
202
dbg. flush ( ) ;
54
203
}
55
204
}
56
205
}
57
-
58
206
/// Configure logging by traversing the crate map and setting the
59
207
/// per-module global logging flags based on the logging spec
60
208
#[ fixed_stack_segment] #[ inline( never) ]
61
209
pub fn init ( crate_map : * u8 ) {
62
- use c_str:: ToCStr ;
63
210
use os;
64
- use ptr;
65
- use option:: { Some , None } ;
66
211
67
212
let log_spec = os:: getenv ( "RUST_LOG" ) ;
68
213
match log_spec {
69
214
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) ;
73
216
}
74
217
None => {
75
- unsafe {
76
- rust_update_log_settings ( crate_map, ptr:: null ( ) ) ;
77
- }
218
+ update_log_settings ( crate_map, ~"") ;
78
219
}
79
220
}
80
221
}
@@ -89,9 +230,101 @@ pub fn console_off() { unsafe { rust_log_console_off() } }
89
230
fn should_log_console ( ) -> bool { unsafe { rust_should_log_console ( ) != 0 } }
90
231
91
232
extern {
92
- fn rust_update_log_settings ( crate_map : * u8 , settings : * libc:: c_char ) ;
93
233
fn rust_log_console_on ( ) ;
94
234
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
+ }
96
285
}
97
286
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