[rCore学习笔记 012]彩色化LOG
实验要求
- 实现分支:ch1
- 完成实验指导书中的内容并在裸机上实现
hello world
输出。 - 实现彩色输出宏(只要求可以彩色输出,不要求 log 等级控制,不要求多种颜色)
- 隐形要求
可以关闭内核所有输出。从 lab2 开始要求关闭内核所有输出(如果实现了 log 等级控制,那么这一点自然就实现了)。 - 利用彩色输出宏输出 os 内存空间布局
输出.text
、.data
、.rodata
、.bss
各段位置,输出等级为INFO
。
ANSI 转义字符
echo -e "\x1b[31mhello world\x1b[0m"
参考资源
官方给出如下资源:对于 Rust, 可以使用 crate log ,推荐参考 rCore
crate log 的应用
打开提供的参考链接,可以了解到应用log
这个特质的方式,看Use这一节,
The basic use of the log crate is through the five logging macros: error!
, warn!
, info!
, debug!
and trace!
where error!
represents the highest-priority log messages and trace!
the lowest. The log messages are filtered by configuring the log level to exclude messages with a lower priority. Each of these macros accept format strings similarly to println!
.
在In libraries里可以看到,我们如果要使用log
,则需要使用crate log
,
Libraries should link only to the log
crate, and use the provided macros to log whatever information will be useful to downstream consumers.
例子
参考Examples,创建一个新的工程来测试它,在/homework/homework-1-log
下用cargo new test_log
创建一个测试工程,在test_log/src
下,万万没想到,这个例子竟然成为了最猛的一集,完美展示了不懂一门语言硬学导致的问题.
Rust
的Crate log
的例子中为:
use log::{info, warn};
pub fn shave_the_yak(yak: &mut Yak) {
info!(target: "yak_events", "Commencing yak shaving for {:?}", yak);
loop {
match find_a_razor() {
Ok(razor) => {
info!("Razor located: {}", razor);
yak.shave(razor);
break;
}
Err(err) => {
warn!("Unable to locate a razor: {}, retrying", err);
}
}
}
}
但是这一段是伪代码,运行的时候是会报错的:
Compiling test_log v0.1.0 (/home/winddevil/workspace/homework/homework-1-log/test_log)
error[E0432]: unresolved import `log`
--> src/main.rs:2:5
|
2 | use log::{info, warn};
| ^^^ use of undeclared crate or module `log`
error[E0412]: cannot find type `Yak` in this scope
--> src/main.rs:3:32
|
3 | pub fn shave_the_yak(yak: &mut Yak)
| ^^^ not found in this scope
error[E0425]: cannot find function `find_a_razor` in this scope
--> src/main.rs:9:15
|
9 | match find_a_razor()
| ^^^^^^^^^^^^ not found in this scope
Some errors have detailed explanations: E0412, E0425, E0432.
For more information about an error, try `rustc --explain E0412`.
error: could not compile `test_log` (bin "test_log") due to 3 previous errors
这就是我们对于Rust
的一种比丁真还要纯真的认识导致的,这时候我们把这个报错error[E0432]: unresolved import 'log'
喂给GPT
,人猿相揖别。只几个石头磨过,小儿时节。
善于使用工具正是一个懒狗所需要的,
遇到错误 error[E0432]: unresolved import 'log'
表示Rust编译器无法找到你尝试引入的log
库。这通常是因为以下几个原因:
- 未添加
log
库为依赖:确保你的Cargo.toml
文件中已经添加了log
库作为依赖。如果没有,你需要在[dependencies]
部分添加它。对于最常见的使用场景,添加如下内容:[dependencies]
log = "0.4" # 或者其它适用的版本号
然后在项目目录下运行 cargo update
来下载依赖。
2. Cargo特性(gated features):如果你的项目或某个依赖启用了特定的Cargo特性,并且这些特性影响了log
的使用,确保正确配置了这些特性。检查Cargo.toml
中的[features]
部分和相关依赖的特性要求。
3. 路径问题:如果你的项目结构比较特殊,可能需要使用相对路径或绝对路径来正确引入log
。但这通常不是导致上述错误的原因,因为log
通常作为crate直接引入。
4. 编译目标或环境问题:确保你的编译目标和环境配置没有排除log
库的编译或链接。这在交叉编译时可能会成为问题。
5. Cargo.lock的干扰:有时候,旧的Cargo.lock
文件可能锁定了不兼容的依赖版本,导致新添加的依赖无法正确解析。尝试删除Cargo.lock
文件(如果有),然后重新运行cargo build
或cargo run
让Cargo重新生成它。
解决方法通常是先检查并修正Cargo.toml
,确保log
库已经被正确添加为依赖,然后更新依赖并重新编译项目。如果问题依旧存在,检查是否有其他依赖或项目配置干扰了log
的正确解析。
What can I say?
答案是Cargo
没学会,这时候我们修改Cargo.toml
,访问log包的文档,可以看到当前的最新版本为0.4.21
,在文件中添加这一段,
[dependencies]
log = "0.4.21"
接下来我们解决error[E0412]: cannot find type 'Yak' in this scope
的问题,这个问题说明Yak
并不是std
里原本有的一个东西,当然也倒逼我们一定要看清楚源代码,这次我们直接读和理解这个报错,说的是在当前scope
即作用域中找不到Yak
,考虑到这个幺蛾子文档的习惯,我们直接在刚才文档的右上角搜索Yak
,
![[Pasted image 20240628015106.png]]
这里我们看到shave it
意为给它剃毛
,考虑原本的代码中的find_a_razor
函数即寻找剃刀
也报错了error[E0425]: cannot find function
find_a_razor in this scope
,那么就很好理解了,Yak
可能是被制造出来的一个例子库,我们尝试在Cargo.toml
中依赖它,
[dependencies]
yak = "0.1.0"
但是很悲剧的是,这个函数并不存在,也就是这段代码纯是一段伪代码,那么我们直接看这段伪代码:
use log::{info, warn};
pub fn shave_the_yak(yak: &mut Yak) {
info!(target: "yak_events", "Commencing yak shaving for {:?}", yak);
loop {
match find_a_razor() {
Ok(razor) => {
info!("Razor located: {}", razor);
yak.shave(razor);
break;
}
Err(err) => {
warn!("Unable to locate a razor: {}, retrying", err);
}
}
}
}
假设Yak
是一个struct
,可以它有一个对应的impl
,是shave
,而还有一个函数find_a_razor
是可以返回一个razor
类型或者一个err
类型的函数.那么我们可以手动补全这段代码:
use std::error::Error;
use std::fmt;
use std::sync::Mutex;
#[derive(Debug)]
pub struct Yak
{
yak_name: String,
is_shaved: bool
}
pub struct razor
{
used_time: u32
}
#[derive(Debug)]
pub struct err
{
context: String
}
impl Error for err
{
}
impl fmt::Display for err
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
{
write!(f, "Error: {}", self.context)
}
}
impl Yak {
pub fn new(yak_name: String) -> Self {
Yak {
yak_name,
is_shaved: false,
}
}
pub fn get_yak_name(&self) -> &str {
&self.yak_name
}
pub fn set_yak_name(&mut self, new_name: String) {
self.yak_name = new_name;
}
pub fn is_shaved(&self) -> bool {
self.is_shaved
}
pub fn shave(&mut self, mut razor: razor)
{
self.is_shaved = true;
razor.used_time+=1;
}
}
impl fmt::Display for razor
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
{
write!(f, "Razor used time: {}", self.used_time)
}
}
pub fn find_a_razor() -> Result<razor,err>
{
let mut one_razor = razor{used_time:0};
if one_razor.used_time<100
{
one_razor.used_time+=1;
Ok(one_razor)
}
else
{
let the_err:err = err{context:"No razor found".to_string()};
Err(the_err)
}
}
那么这段代码里就含有几个概念没有搞定,补全过程中极尽折磨:
- 枚举类型的泛型
- 分支结构
- Debug输出
枚举类型
这段代码中,Result<razor,err>
是用到了枚举和泛型.
首先Rust
的枚举更像是C
的枚举的增强版本,C
的枚举的几个成员是一个整数,可以按照默认的0-N
来用,一般和switch
结合增强状态机的可读性.但是很明显这样的成员很难满足Rust
的灵活性的需求,尤其是这个场景,又要返回Ok(razor)
类型,又要返回Err(err)
类型.
那么可以理解Rust
的枚举,它不只是为了用一系列代号代表这个类型的所有的可能,而且这个类型可能对应很多种基础类型,也可以被代表.
这样这一段可以理解了,Result<T,E>
是一个枚举类型,源代码我们可以在这里找到:
pub enum Result<T, E> {
Ok(T),
Err(E),
}
可能还是比较难理解,因为这里掺杂了泛型:
pub enum OBJ
{
A(u32),
B(u64)
}
上边这段代码代表OBJ
只可能是A
或者B
,两种情况,但是A
实际上包裹
---可以理解为A
是u32
类型的一个代号,但是不是直接的u32
而是A
携带了这样一个变量,B
也同理.
考虑到使用了泛型,这就意味着我们可以自定义Ok
和Err
各自包裹着什么.
分支结构
这里主要卡住人的是一点,rust
的if
后边的条件是没有括号的.
Debug输出
如果使用了{:?}
这种输出,则需要为这个需要输出的类实现一个Debug
特性,或者使用#[derive(Debug)]
.
Cargo的输出等级
编辑main.rs
之后发现不能输出:
mod yak;
use log::{info, warn};
use yak::{Yak,find_a_razor};
pub fn shave_the_yak(yak: &mut Yak)
{
info!(target: "yak_events", "Commencing yak shaving for {:?}", yak);
loop
{
match find_a_razor()
{
Ok(razor) =>
{
info!("Razor located: {}", razor);
yak.shave(razor);
break;
}
Err(err) =>
{
warn!("Unable to locate a razor: {}, retrying", err);
}
}
}
}
fn main()
{
env_logger::init();
let mut yak = Yak::new("Fred".to_string());
shave_the_yak(&mut yak);
println!("Hello, world!");
}
这里是存在两个问题:
- 错误信息没有输出到工作台
Cargo
的输出没有到info
这个级别
输出到工作台
编辑Cargo.toml
增加一个依赖:
[package]
name = "test_log"
version = "0.1.0"
edition = "2021"
[dependencies]
log = "0.4.21"
env_logger = "0.8"
编辑main.rs
,使用env_logger::init();
即可.
修改cargo
的debug
等级
使用这个方法即可:
RUST_LOG=info cargo run
给os
工程适配log
我们首先看题目中给的参考代码:
use core::fmt;
use lazy_static::lazy_static;
use log::{self, Level, LevelFilter, Log, Metadata, Record};
use crate::sync::SpinNoIrqLock as Mutex;
lazy_static! {
static ref LOG_LOCK: Mutex<()> = Mutex::new(());
}
pub fn init() {
static LOGGER: SimpleLogger = SimpleLogger;
log::set_logger(&LOGGER).unwrap();
log::set_max_level(match option_env!("LOG") {
Some("error") => LevelFilter::Error,
Some("warn") => LevelFilter::Warn,
Some("info") => LevelFilter::Info,
Some("debug") => LevelFilter::Debug,
Some("trace") => LevelFilter::Trace,
_ => LevelFilter::Off,
});
}
#[macro_export]
macro_rules! print {
($($arg:tt)*) => ({
$crate::logging::print(format_args!($($arg)*));
});
}
#[macro_export]
macro_rules! println {
($fmt:expr) => (print!(concat!($fmt, "\n")));
($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
}
/// Add escape sequence to print with color in Linux console
macro_rules! with_color {
($args: ident, $color_code: ident) => {{
format_args!("\u{1B}[{}m{}\u{1B}[0m", $color_code as u8, $args)
}};
}
fn print_in_color(args: fmt::Arguments, color_code: u8) {
use crate::arch::io;
let _guard = LOG_LOCK.lock();
io::putfmt(with_color!(args, color_code));
}
pub fn print(args: fmt::Arguments) {
use crate::arch::io;
let _guard = LOG_LOCK.lock();
io::putfmt(args);
}
struct SimpleLogger;
impl Log for SimpleLogger {
fn enabled(&self, _metadata: &Metadata) -> bool {
true
}
fn log(&self, record: &Record) {
if !self.enabled(record.metadata()) {
return;
}
/*
if let Some(tid) = processor().tid_option() {
print_in_color(
format_args!(
"[{:>5}][{},{}] {}\n",
record.level(),
crate::arch::cpu::id(),
tid,
record.args()
),
level_to_color_code(record.level()),
);
} else {
*/
print_in_color(
format_args!(
"[{:>5}][{},-] {}\n",
record.level(),
crate::arch::cpu::id(),
record.args()
),
level_to_color_code(record.level()),
);
//}
}
fn flush(&self) {}
}
fn level_to_color_code(level: Level) -> u8 {
match level {
Level::Error => 31, // Red
Level::Warn => 93, // BrightYellow
Level::Info => 34, // Blue
Level::Debug => 32, // Green
Level::Trace => 90, // BrightBlack
}
}
我们可以看到参考代码自己实现了一个SimpleLogger
类型,并且给它实现了Log
这个特性,这是起初很让人难以理解的,但是这让我们想到一个细节,即我们实际上使用了env_logger
这个库,用一句看似简单的初始化完成了我们的任务,即把info
等宏和硬件输出联系在一起,所以因此我们也需要自己去实现一个带有Log
特性的SimpleLogger
类,由于我们有了参考源码,实际上的工作反而没有那么困难.
[[012 彩色化LOG#^bf99b4|这里]]实际上又进行了一个简单的Log
特质的实现,因为思路先后问题,写到了下一节.
简单的Log特质的实现
^bf99b4
可以在这里看到Log
特质实现的时候需要实现的方法为enabled
,log
,flush
.并且可以在这里看到一个小的样例,
use log::{Record, Level, Metadata};
struct SimpleLogger;
impl log::Log for SimpleLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= Level::Info
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
println!("{} - {}", record.level(), record.args());
}
}
fn flush(&self) {}
}
可以根据在在这里看到Log
特质实现的时候需要实现的方法,
enabled
方法是传入一个Metadata
的引用返回一个bool
的函数,用于判断当前的这个数据是不是应该被logger
给记录下来,样例里简单地写成了只需要<=Level::Info
即可.log
方法是传入一个Record
,因为Rcord
里存在很多信息,然后对它进行判定,然后按照我们需要的方式来进行日志记录.这里就是直接判断等级是不是够了,然后输出其level
和args
flush
方法:- 在Rust的日志记录系统中,特别是使用
log
库和相关的宏如info!
,warn!
,error!
等进行日志记录时,这些宏会调用日志框架来记录消息。通常情况下,当你的应用程序正常退出时,日志框架会自动处理缓冲区中的日志记录,确保所有未提交的日志条目都被写入到日志目的地,比如控制台或文件。 - 然而,有时候应用程序可能在异常情况下终止,或者日志框架的默认行为可能不足以满足你的需求。在这种情况下,日志框架通常会提供一个方法,允许你显式地触发日志的刷新,以确保所有日志记录都被正确处理。这个方法通常称为
flush
。 - 在这个例子里,因为我们的日志是直接输出到命令行的,因此只需要实现为空即可.
- 在Rust的日志记录系统中,特别是使用
为一个Logger
类实现Log
特质之后,我们可以直接使用info!
等宏来实现输出.
参照参考代码写出来的Logger
如下:
use core::fmt;
use log::{self,Level,LevelFilter,Metadata,Log,Record};
struct SimpleLogger;
impl Log for SimpleLogger
{
fn enabled(&self, metadata: &Metadata) -> bool
{
true
}
fn log(&self, record: &Record)
{
if !self.enabled(record.metadata())
{
return;
}
print_in_color(
format_args!(
"[{:>5}] {}\n",
record.level(),
record.args()
),
level_to_color_code(record.level()),
);
}
fn flush(&self) {}
}
fn print_in_color(args: fmt::Arguments, color_code: u8)
{
println!("\u{1B}[{}m{}\u{1B}[0m", color_code, args);
}
fn level_to_color_code(level: Level) -> u8 {
match level {
Level::Error => 31, // Red
Level::Warn => 93, // BrightYellow
Level::Info => 34, // Blue
Level::Debug => 32, // Green
Level::Trace => 90, // BrightBlack
}
}
pub fn init()
{
static LOGGER: SimpleLogger = SimpleLogger;
log::set_logger(&LOGGER).unwrap();
// log::set_max_level(LevelFilter::Trace);
log::set_max_level(match option_env!("LOG") {
Some("error") => LevelFilter::Error,
Some("warn") => LevelFilter::Warn,
Some("info") => LevelFilter::Info,
Some("debug") => LevelFilter::Debug,
Some("trace") => LevelFilter::Trace,
_ => LevelFilter::Off,
});
}
写出来的main.rs
如下:
// os/src/main.rs
#![no_std]
#![no_main]
#![feature(panic_info_message)]
use core::{arch::global_asm};
use delay::sleep;
use log::{debug, info, error};
#[macro_use]
mod console;
mod sbi;
mod lang_items;
mod delay;
mod logging;
//use sbi::shutdown;
global_asm!(include_str!("entry.asm"));
#[no_mangle]
pub fn rust_main() -> ! {
extern "C" {
fn stext(); // begin addr of text segment
fn etext(); // end addr of text segment
fn srodata(); // start addr of Read-Only data segment
fn erodata(); // end addr of Read-Only data ssegment
fn sdata(); // start addr of data segment
fn edata(); // end addr of data segment
}
clear_bss();
logging::init();
println!("Hello World");
info!(".text [{:#x}, {:#x})", stext as usize, etext as usize);
debug!(".rodata [{:#x}, {:#x})", srodata as usize, erodata as usize);
error!(".data [{:#x}, {:#x})", sdata as usize, edata as usize);
panic!("Shutdown machine!");
}
fn clear_bss() {
extern "C" {
fn sbss();
fn ebss();
}
(sbss as usize..ebss as usize).for_each(|a| {
unsafe { (a as *mut u8).write_volatile(0) }
});
}
这里只讲关键点:
\u{1B}[{}m{}\u{1B}[0m
里的{}
为占位符,第一个{}
对应的是输出的颜色,第二个是输出的内容format_args!
这个宏是一个非常神奇的宏,可以实现传入一个字符串,然后把占位符用后边传入的参数实现,就像C
里边printf
的格式化和python
里边f"string{var}".
- 在运行
cargo
时,增加env LOG="info"
,可以有效传入环境变量参数,注意不要搞错大小写什么的以至于贻笑大方,这里注意不是在运行qemu
的时候这么做.
[rCore学习笔记 012]彩色化LOG的更多相关文章
- java学习笔记12--国际化
java学习笔记12--国际化 国际化的操作就是指一个程序可以同时适应多门语言,即:如果现在程序者是中国人,则会以中文为显示文字,如果现在程序的使用者是英国人,则会以英语为显示的文字,也就是说可以通过 ...
- CNN学习笔记:池化层
CNN学习笔记:池化层 池化 池化(Pooling)是卷积神经网络中另一个重要的概念,它实际上是一种形式的降采样.有多种不同形式的非线性池化函数,而其中“最大池化(Max pooling)”是最为常见 ...
- Python学习笔记012——装饰器
1 装饰器 1.1装饰器定义 在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator). 1.2 装饰器分类 装饰器:函数装饰器,类装饰器,函数的装饰器,类的装饰器 装饰器:函数装饰函 ...
- React学习笔记-5-初始化阶段介绍
初始化阶段可以使用的函数:getDefaultProps:只调用一次,实例之间共享引用.只有在组件的第一个实例被初始化的时候,才会调用他,然后react会把这个函数的返回结果保存起来,从第二个实例开始 ...
- Yii2学习笔记:汉化yii,设置表单的描述(属性标签attributeLabels)
一:汉化框架 框架汉化在这里设置,如果不生效,前台后台的配置文件都设置下应该就可以了 二:汉化表单 汉化表单,直接在模型层设置,例如: 原来的联系我们表单 汉化后: ] 这种汉化在哪里修改呢?其实是设 ...
- python自动化学习笔记11-自动化测试UTP框架
前面基本的unittest及ddt已经学过了,现在我们系统把这些知识结合起来,写一个简单的UTP自动化测试框架: 我们先来建基础目录,首先新建一个项目,项目下建父目录UTP,conf目录,用来存放配置 ...
- SAS学习笔记63 如何导出Log
如上,将Log输出,然后又恢复到SAS系统里面的Log,把需要运行的程序放到他们中间就可以了.这种方法不会出现Log打印满了的情况 这种是先输出在SAS系统里面,然后在输出,在SAS里面Log的行是有 ...
- Vue.js官方文档学习笔记(二)组件化应用的构建
组件化应用的构建 组件化应用允许我们使用小型.独立和通常可复用的组件构建大型应用. Vue注册组件 Vue.component('todo-item',{template:'<li>这是个 ...
- Selenium学习笔记之外部化相关测试数据---xml
我们也可以用xml来定义一个信息更为丰富的UIMap.xml文件,比如,额外还添加控件所属的页,控件的类型,然后解析构建一个XMLParser类来读取相应的值. <?xml version=&q ...
- ASP.NET本质论第一章网站应用程序学习笔记3-对象化的Http
在.NET环境下,万物皆对象,在HttpRuntime收到请求之后,立即将通过HttpWorkerRequest传递的参数进行分析和分解,创建方便用于网站应用程序处理用的对象,其中主要涉及到两个对象类 ...
随机推荐
- tcc-transaction源码详解
更多优秀博文,请关注博主的个人博客:听到微笑的博客 本文主要介绍TCC的原理,以及从代码的角度上分析如何实现的:不涉及具体使用示例.本文通过分析tcc-transaction源码带大家了解TCC分布式 ...
- 8.17考试总结(NOIP模拟42)[卷·简单题·粉丝·字符串]
你的败因只有一个,就是与我为敌. T1 卷 解题思路 乍一看,简单的树形 DP . 后来一看数据范围,发现事实并非如此.(\((10^9)^{2\times 10^5}\)????) 毕竟取 \( ...
- 【深度学习】c++部署onnx模型(Yolov5、PP-HumanSeg、GoogLeNet、UNet)
这两天部署了好多模型,记录一下.代码链接. onnxruntime在第一张图的推理上比opencv快很多,但在后面的图上略微慢了一点. 不同的模型的部署时的输出不同,处理时需要对输出比较了解,下面分别 ...
- Web 页面性能衡量指标-以用户为中心的效果指标
Web 页面性能衡量指标-以用户为中心的性能指标 以用户为中心的性能指标是理解和改进站点体验的关键点 一.以用户为中心的性能指标 1. 指标是用来干啥的? 指标是用来衡量性能和用户体验的 2. 指标类 ...
- RHCSA认证学习_尝试
第一天 一,linux系统的基本概念以及命令终端字段含义介绍 linux系统的基本概念: ·多用户的系统:允许同时执行多个任务 ·多任务的系统:允许同时执行多个任务 ·严格区分大小写:命令,选项,参数 ...
- IDEA顺序启动多个Spring Boot微服务
上个月公司新开发的一个项目,需要使用微服务,将单体服务拆分成多个微服务.但是每次修改代码之后都需要启动多个微服务,改个代码,都要修改五分钟,启动半小时,但是idea可以设置将多个服务依次启动,减少操作 ...
- 面试必会 --> MyBatis篇
什么是MyBatis Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动.创建连接.创建statement等繁杂的过程 ...
- INFINI Labs 产品更新 | Console 告警中心 UI 全新改版,新增 Dashboard 全屏模式等功能
本次 INFINI Labs 产品更新主要发布 Console v1.7.0,重点优化了 Console 告警中心和数据看板 Dashboard 可视化功能.详细介绍如下: 优化告警中心 UI 上个版 ...
- BC6-牛牛的第二个整数
题目描述 牛牛从键盘上输入三个整数,并尝试在屏幕上显示第二个整数. 输入描述 一行输入 3 个整数,用空格隔开. 输出描述 请输出第二个整数的值. 示例 1 输入:1 2 3 输出:2 解题思路 方案 ...
- RequestBodyAdvice和注解方式进行统一参数处理demo
RequestBodyAdvice和注解方式进行统一参数处理demo @Target({ ElementType.METHOD, ElementType.TYPE }) @Retention(Rete ...