Skip to content

Commit 7a32530

Browse files
melodyoncodehoe-jo
authored andcommitted
Optimize debug log in cpp parser
1 parent b08a181 commit 7a32530

8 files changed

Lines changed: 214 additions & 83 deletions

File tree

cpp/libclang/BUILD

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,22 @@
1010
#
1111
# SPDX-License-Identifier: Apache-2.0
1212
# *******************************************************************************
13+
load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
1314
load("@rules_rust//rust:defs.bzl", "rust_binary")
1415

16+
string_flag(
17+
name = "log_level",
18+
build_setting_default = "error",
19+
values = [
20+
"error",
21+
"warn",
22+
"info",
23+
"debug",
24+
"trace",
25+
],
26+
visibility = ["//visibility:public"],
27+
)
28+
1529
rust_binary(
1630
name = "clang_rs_parser",
1731
srcs = ["src/main.rs"],
@@ -23,9 +37,12 @@ rust_binary(
2337
},
2438
visibility = ["//visibility:public"],
2539
deps = [
40+
"//cpp/libclang/src/utils",
2641
"//cpp/libclang/src/visitor:visit_tu",
2742
"@crates//:clang",
2843
"@crates//:clap",
44+
"@crates//:env_logger",
45+
"@crates//:log",
2946
"@crates//:serde_json",
3047
],
3148
)

cpp/libclang/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,16 @@ Expected result:
3838
- Bazel creates parser output artifact:
3939
- `bazel-bin/cpp/libclang/cpp_parser_include_3rdparty_result.json`
4040

41+
## Configure debug logging
42+
43+
To enable debug output for parser actions, set the Bazel build setting:
44+
45+
```bash
46+
bazel build //cpp/libclang/integration_test/cases/include_3rdparty:parser --//cpp/libclang:log_level=debug
47+
```
48+
49+
Accepted values are: `error`, `warn`, `info`, `debug`, `trace`.
50+
4151
## Quick check (optional)
4252

4353
```bash

cpp/libclang/cpp_parser.bzl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#
1111
# SPDX-License-Identifier: Apache-2.0
1212
# *******************************************************************************
13+
load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
1314
load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cc_toolchain", "use_cc_toolchain")
1415
load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
1516

@@ -237,6 +238,7 @@ def _cpp_parser_impl(ctx):
237238
arguments = args,
238239
env = {
239240
"LIBCLANG_PATH": libclang.dirname,
241+
"LIBCLANG_LOG": ctx.attr._log_level[BuildSettingInfo].value,
240242
},
241243
mnemonic = "CppAnalyze",
242244
# this is required to parse some system headers
@@ -279,6 +281,10 @@ cpp_parser = rule(
279281
doc = "LLVM toolchain filegroup containing the arch-specific __config_site file " +
280282
"(include/<triple>/c++/v1/__config_site) used to locate the ABI include path.",
281283
),
284+
"_log_level": attr.label(
285+
default = Label("//cpp/libclang:log_level"),
286+
doc = "Build setting that controls clang_rs_parser log level.",
287+
),
282288
},
283289
toolchains = use_cc_toolchain(),
284290
fragments = ["cpp"],

cpp/libclang/src/main.rs

Lines changed: 54 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@
1010
//
1111
// SPDX-License-Identifier: Apache-2.0
1212
////////////////////////////////////////////////////////////////////////////////////
13-
use clang::{Entity, EntityKind, EntityVisitResult};
1413
use clap::Parser as ClapParser;
14+
use env_logger::Builder;
15+
use log::{debug, error, LevelFilter};
1516
use std::collections::BTreeMap;
1617
use std::fs;
17-
use std::fs::OpenOptions;
18-
use std::io::{BufWriter, Write};
1918
use std::path::PathBuf;
19+
20+
use utils::{render_entity_tree, write_entity_tree};
2021
use visit_tu::context;
2122
use visit_tu::visitor;
2223
use visit_tu::{FunctionDef, VisitContext, Visitor};
@@ -41,10 +42,6 @@ struct Args {
4142
/// Output JSON format for debugging (internal use only)
4243
#[arg(long, hide = true)]
4344
json: bool,
44-
45-
/// Print verbose output
46-
#[arg(short, long)]
47-
verbose: bool,
4845
}
4946

5047
fn parse_file(
@@ -55,11 +52,11 @@ fn parse_file(
5552
all_classes: &mut BTreeMap<String, context::TypeMapValue>,
5653
all_functions: &mut Vec<FunctionDef>,
5754
) {
58-
println!("Parsing TU: {:?}", file);
55+
debug!("Parsing TU: {:?}", file);
5956

6057
if let Some(path_str) = file.to_str() {
6158
if visitor::is_external_dependency_path(path_str) {
62-
println!(" Skipping external dependency file: {:?}", file);
59+
debug!("Skipping external dependency file: {:?}", file);
6360
return;
6461
}
6562
};
@@ -68,62 +65,88 @@ fn parse_file(
6865

6966
match parse_result {
7067
Ok(parsed) => {
71-
println!(" Parsed successfully, parsed is {:?}", parsed);
72-
7368
let diagnostics = parsed.get_diagnostics();
7469
if !diagnostics.is_empty() {
75-
println!(" Diagnostics: {}", diagnostics.len());
70+
debug!("Diagnostics: {}", diagnostics.len());
71+
for diagnostic in &diagnostics {
72+
debug!("Diagnostic: {:?}", diagnostic);
73+
}
7674
}
7775

7876
let entity = parsed.get_entity();
79-
print_entity(&entity, 0, PrintMode::File(ast_file_output_path));
80-
print_entity(&entity, 0, PrintMode::Stdout);
77+
debug!("Parsed {:?} successfully", parsed);
78+
if log::log_enabled!(log::Level::Trace) {
79+
let entity_tree = render_entity_tree(&entity, 0);
80+
write_entity_tree(ast_file_output_path, &entity_tree);
81+
}
8182

8283
let mut ctx = VisitContext::default();
8384
let mut visitor = Visitor::new(&mut ctx);
8485
visitor.visit(entity);
85-
println!(
86-
" Visited TU, extracted {} classes, {} functions",
86+
debug!(
87+
"Visited TU, extracted {} classes, {} functions",
8788
ctx.types.len(),
8889
ctx.functions.len()
8990
);
9091
for (class_name, logic_class) in &ctx.types {
91-
println!(" - class: {}", class_name);
92-
println!("{:#?}", logic_class);
92+
debug!("Class {}:\n{:#?}", class_name, logic_class);
9393
all_classes.insert(class_name.clone(), logic_class.clone());
9494
}
9595
all_functions.extend(ctx.functions);
9696
}
9797
Err(e) => {
98-
eprintln!(" Failed to parse {:?}: {:?}", file, e);
98+
error!("Failed to parse {:?}: {:?}", file, e);
9999
}
100100
}
101101
}
102102

103-
fn main() -> Result<(), Box<dyn std::error::Error>> {
104-
println!("=== libclang Information ===\n");
105-
println!("Command line: {:?}", std::env::args().collect::<Vec<_>>());
103+
fn parse_log_level_from_env() -> LevelFilter {
104+
std::env::var("LIBCLANG_LOG")
105+
.ok()
106+
.and_then(|value| value.parse::<LevelFilter>().ok())
107+
.unwrap_or(LevelFilter::Error)
108+
}
109+
110+
fn init_logging() {
111+
Builder::new()
112+
.filter_level(parse_log_level_from_env())
113+
.init();
114+
}
115+
116+
fn init_libclang() -> clang::Clang {
117+
debug!("=== libclang Information ===");
118+
debug!("Command line: {:?}", std::env::args().collect::<Vec<_>>());
106119

107120
if let Ok(path) = std::env::var("LIBCLANG_PATH") {
108-
println!("LIBCLANG_PATH: {}", path);
121+
debug!("LIBCLANG_PATH: {}", path);
109122
}
110123

111-
// Load clang - keep it alive for the entire scope
112124
let clang = match clang::Clang::new() {
113125
Ok(c) => {
114-
println!("Successfully loaded libclang\n");
126+
debug!("Successfully loaded libclang");
115127
c
116128
}
117129
Err(e) => {
118-
eprintln!("Failed to load libclang: {}", e);
130+
error!("Failed to load libclang: {}", e);
119131
std::process::exit(1);
120132
}
121133
};
122134

123-
// All operations use the clang-rs wrapper API
124-
let index = clang::Index::new(&clang, false, true);
125-
println!("✓ Created clang index");
126-
println!("libclang version: {}", clang::get_version());
135+
debug!("libclang version: {}", clang::get_version());
136+
debug!("Using Bazel's LLVM toolchain with clang-rs wrapper");
137+
clang
138+
}
139+
140+
fn init_clang_index(clang: &clang::Clang) -> clang::Index {
141+
let index = clang::Index::new(clang, false, true);
142+
debug!("Created clang index");
143+
index
144+
}
145+
146+
fn main() -> Result<(), Box<dyn std::error::Error>> {
147+
init_logging();
148+
let clang = init_libclang();
149+
let index = init_clang_index(&clang);
127150

128151
let command_line_args = Args::parse();
129152
let mut all_classes = BTreeMap::new();
@@ -155,59 +178,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
155178
);
156179
let output_json = serde_json::to_string_pretty(&output)?;
157180
fs::write(&command_line_args.output, output_json)?;
158-
println!("Wrote AST JSON to {:?}", command_line_args.output);
159-
println!("\n Using Bazel's LLVM toolchain with clang-rs wrapper!");
181+
debug!("Wrote AST JSON to {:?}", command_line_args.output);
160182

161183
Ok(())
162184
}
163-
164-
enum PrintMode<'a> {
165-
Stdout,
166-
File(&'a PathBuf),
167-
}
168-
169-
fn print_entity(entity: &Entity, level: usize, print_mode: PrintMode) {
170-
match print_mode {
171-
PrintMode::Stdout => {
172-
let mut stdout = std::io::stdout().lock();
173-
print_entity_to(entity, level, &mut stdout)
174-
}
175-
PrintMode::File(path) => {
176-
let file = OpenOptions::new()
177-
.create(true)
178-
.append(true)
179-
.open(path)
180-
.unwrap_or_else(|e| {
181-
panic!("Failed to open file {:?} for writing: {}", path, e);
182-
});
183-
let mut file_out = BufWriter::new(file);
184-
print_entity_to(entity, level, &mut file_out)
185-
}
186-
}
187-
}
188-
189-
fn print_entity_to(entity: &Entity, level: usize, out: &mut dyn Write) {
190-
let indent = " ".repeat(level);
191-
let kind = entity.get_kind();
192-
let spelling = match kind {
193-
EntityKind::AccessSpecifier => entity
194-
.get_accessibility()
195-
.map(|a| format!("{:?}", a).to_lowercase())
196-
.unwrap_or_default(),
197-
_ => entity.get_name().unwrap_or_default(),
198-
};
199-
let output = format!("{}{:?} | {}\n", indent, kind, spelling);
200-
201-
// we soft fail since this is just a diagnostic print and should not crush the main programm
202-
out.write_all(output.as_bytes()).unwrap_or_else(|e| {
203-
eprintln!("Failed to write entity info: {}", e);
204-
});
205-
out.flush().unwrap_or_else(|e| {
206-
eprintln!("Failed to flush output: {}", e);
207-
});
208-
209-
entity.visit_children(|child, _parent| {
210-
print_entity_to(&child, level + 1, out);
211-
EntityVisitResult::Continue
212-
});
213-
}

cpp/libclang/src/utils/BUILD

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# *******************************************************************************
2+
# Copyright (c) 2026 Contributors to the Eclipse Foundation
3+
#
4+
# See the NOTICE file(s) distributed with this work for additional
5+
# information regarding copyright ownership.
6+
#
7+
# This program and the accompanying materials are made available under the
8+
# terms of the Apache License Version 2.0 which is available at
9+
# https://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# SPDX-License-Identifier: Apache-2.0
12+
# *******************************************************************************
13+
load("@rules_rust//rust:defs.bzl", "rust_library")
14+
15+
rust_library(
16+
name = "utils",
17+
srcs = [
18+
"lib.rs",
19+
"render.rs",
20+
"write.rs",
21+
],
22+
visibility = ["//cpp/libclang:__pkg__"],
23+
deps = [
24+
"@crates//:clang",
25+
"@crates//:log",
26+
],
27+
)

cpp/libclang/src/utils/lib.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
///////////////////////////////////////////////////////////////////////////////////
2+
// Copyright (c) 2026 Contributors to the Eclipse Foundation
3+
//
4+
// See the NOTICE file(s) distributed with this work for additional
5+
// information regarding copyright ownership.
6+
//
7+
// This program and the accompanying materials are made available under the
8+
// terms of the Apache License Version 2.0 which is available at
9+
// https://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
////////////////////////////////////////////////////////////////////////////////////
13+
mod render;
14+
mod write;
15+
16+
pub use render::render_entity_tree;
17+
pub use write::write_entity_tree;

cpp/libclang/src/utils/render.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
///////////////////////////////////////////////////////////////////////////////////
2+
// Copyright (c) 2026 Contributors to the Eclipse Foundation
3+
//
4+
// See the NOTICE file(s) distributed with this work for additional
5+
// information regarding copyright ownership.
6+
//
7+
// This program and the accompanying materials are made available under the
8+
// terms of the Apache License Version 2.0 which is available at
9+
// https://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
////////////////////////////////////////////////////////////////////////////////////
13+
use clang::{Entity, EntityKind, EntityVisitResult};
14+
15+
pub fn render_entity_tree(entity: &Entity, level: usize) -> String {
16+
let mut output = String::new();
17+
append_entity_tree(entity, level, &mut output);
18+
output
19+
}
20+
21+
fn append_entity_tree(entity: &Entity, level: usize, output: &mut String) {
22+
output.push_str(&format_entity_line(entity, level));
23+
output.push('\n');
24+
25+
entity.visit_children(|child, _parent| {
26+
append_entity_tree(&child, level + 1, output);
27+
EntityVisitResult::Continue
28+
});
29+
}
30+
31+
fn format_entity_line(entity: &Entity, level: usize) -> String {
32+
let indent = " ".repeat(level);
33+
let kind = entity.get_kind();
34+
let spelling = match kind {
35+
EntityKind::AccessSpecifier => entity
36+
.get_accessibility()
37+
.map(|a| format!("{:?}", a).to_lowercase())
38+
.unwrap_or_default(),
39+
_ => entity.get_name().unwrap_or_default(),
40+
};
41+
42+
format!("{}{:?} | {}", indent, kind, spelling)
43+
}

0 commit comments

Comments
 (0)