首页 文章 精选 留言 我的

精选列表

搜索[协同工作],共10000篇文章
优秀的个人博客,低调大师

原来 Rust 当然 Lint 是这样工作的

Rustc 源码学习 - Lint 与 LintPass 时间:2022.8.15 撰稿:张正@KusionStack开发组 转载请注明原文链接:https://mp.weixin.qq.com/s/kmXNRgDZxkOgtxZtAyDSOA 背景 在 KusionStack 技术栈中, KCL 配置策略语言是重要的组成部分之一。为了帮助用户更好的编写 KCL 代码,我们也为 KCL 语言开发了一些语言工具,Lint 就是其中一种。Lint 工具帮助用户检查代码中潜在的问题和错误,同时也可以用于自动化的代码检查,保障仓库代码规范和质量。因为 KCL 语言由 Rust 实现,一些功能也学习和参考了 Rustc。本文是在学习 Rustc 过程中的一些思考和沉淀,在这里做一些分享。 Rustc Rustc 是 Rust Compiler 的简称,即 Rust 编程语言的编译器。Rust 的编译器是自举的,即 Rustc 由 Rust 语言编写而成,可以通过旧版本编译出新版本。因此,Rustc 可以说是用 Rust 语言编写编译器的最佳实践。 Lint 工具 Lint 是代码静态分析工具的一种,最早是来源于 C 语言。Lint 工具通常会检查代码中潜在的问题和错误,包括(但不限于)编程风格(缩进、空行、空格)、代码质量(定义未使用的变量、文档缺失)以及错误代码(除0错误、重复定义、循环引用)等问题。通常来说,Lint 工具除了标识错误外,还会带有一定的 fix/refactor suggest 和 auto-fix 的能力。在工程中引入 Lint 工具可以有效的减少错误,提高整体的工程质量。此外,对一种编程语言来说,Lint 工具通常也是其他工具研发的前置条件,例如 IDE 插件的错误提示,CI 的 Pipeline 检测等。 Lint vs. LintPass 概念与关系 Rustc 中关于 Lint 最主要的结构有两个, Lint 和 LintPass。首先需要区分 Lint 和 LintPass 的概念。Rustc 的很多文档中都将它们统称为 Lint,这很容易造成混淆。关于这两者之间的区别,rustc-dev-guide 给出的解释是: Lint declarations don't carry any "state" - they are merely global identifiers and descriptions of lints. We assert at runtime that they are not registered twice (by lint name). Lint passes are the meat of any lint. 从定义方面, Lint 是对所定义的 lint 检查的静态描述,例如 name, level, description, code 等属性,与检查时的状态无关,Rustc 用 Lint 的定义做唯一性的检查。而 LintPass 是 Lint 的具体实现,是在检查时调用的 check_* 方法。 在具体的代码实现方法, Lint定义为一个 Struct,所有 lint 的定义都是此类型的一个实例/对象。而 LintPass 则对应为一个 trait。trait 类似于 java/c++ 中的接口,每一个 lintpass 的定义都需要实现该接口中定义的方法。 /// Specification of a single lint. #[derive(Copy, Clone, Debug)] pub struct Lint { pub name: &'static str, /// Default level for the lint. pub default_level: Level, /// Description of the lint or the issue it detects. /// /// e.g., "imports that are never used" pub desc: &'static str, ... } pub trait LintPass { fn name(&self) -> &'static str; } 需要注意的是,尽管刚刚的描述中说到trait 类似于接口而 Lint 是一个 struct,但 Lint 和 LintPass 之间并不是 OO 中一个“类”和它的“方法”的关系。而是在声明 LintPass 会生成一个实现了该 trait 的同名的 struct,该 struct 中的 get_lints() 方法会生成对应的 Lint 定义。 这与 rustc-dev-guide 的描述也保持了一致: A lint might not have any lint pass that emits it, it could have many, or just one -- the compiler doesn't track whether a pass is in any way associated with a particular lint, and frequently lints are emitted as part of other work (e.g., type checking, etc.). Lint 与 LintPass 的宏定义 Rustc 为 Lint 和 LintPass 都提供了用于定义其结构的宏。 定义 Lint 的宏declare_lint 比较简单,可以在rustc_lint_defs::lib.rs中找到。declare_lint 宏解析输入参数,并生成名称为 $NAME 的 Lint struct。 #[macro_export] macro_rules! declare_lint { ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr) => ( $crate::declare_lint!( $(#[$attr])* $vis $NAME, $Level, $desc, ); ); ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr, $(@feature_gate = $gate:expr;)? $(@future_incompatible = FutureIncompatibleInfo { $($field:ident : $val:expr),* $(,)* }; )? $($v:ident),*) => ( $(#[$attr])* $vis static $NAME: &$crate::Lint = &$crate::Lint { name: stringify!($NAME), default_level: $crate::$Level, desc: $desc, edition_lint_opts: None, is_plugin: false, $($v: true,)* $(feature_gate: Some($gate),)* $(future_incompatible: Some($crate::FutureIncompatibleInfo { $($field: $val,)* ..$crate::FutureIncompatibleInfo::default_fields_for_macro() }),)* ..$crate::Lint::default_fields_for_macro() }; ); ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr, $lint_edition: expr => $edition_level: ident ) => ( $(#[$attr])* $vis static $NAME: &$crate::Lint = &$crate::Lint { name: stringify!($NAME), default_level: $crate::$Level, desc: $desc, edition_lint_opts: Some(($lint_edition, $crate::Level::$edition_level)), report_in_external_macro: false, is_plugin: false, }; ); } LintPass 的定义涉及到两个宏: declare_lint_pass:生成一个名为$name 的 struct,并且调用 impl_lint_pass 宏。 macro_rules! declare_lint_pass { ($(#[$m:meta])* $name:ident => [$($lint:expr),* $(,)?]) => { $(#[$m])* #[derive(Copy, Clone)] pub struct $name; $crate::impl_lint_pass!($name => [$($lint),*]); }; } impl_lint_pass:为生成的 LintPass 结构实现fn name()和 fn get_lints() 方法。 macro_rules! impl_lint_pass { ($ty:ty => [$($lint:expr),* $(,)?]) => { impl $crate::LintPass for $ty { fn name(&self) -> &'static str { stringify!($ty) } } impl $ty { pub fn get_lints() -> $crate::LintArray { $crate::lint_array!($($lint),*) } } }; } EarlyLintPass 与 LateLintPass 前面关于 LintPass 的宏之中,只定义了fn name()和 fn get_lints() 方法,但并没有定义用于检查的 check_* 函数。这是因为 Rustc 中将 LintPass 分为了更为具体的两类:EarlyLintPass和LateLintPass。其主要区别在于检查的元素是否带有类型信息,即在类型检查之前还是之后执行。例如, WhileTrue 检查代码中的 while true{...} 并提示用户使用 loop{...} 去代替。这项检查不需要任何的类型信息,因此被定义为一个 EarlyLint(代码中 impl EarlyLintPass for WhileTrue。 declare_lint! { WHILE_TRUE, Warn, "suggest using `loop { }` instead of `while true { }`" } declare_lint_pass!(WhileTrue => [WHILE_TRUE]); impl EarlyLintPass for WhileTrue { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { ... } } Rustc 中用了3个宏去定义 EarlyLintPass: early_lint_methods:early_lint_methods 中定义了 EarlyLintPass 中需要实现的 check_*函数,并且将这些函数以及接收的参数 $args传递给下一个宏。 macro_rules! early_lint_methods { ($macro:path, $args:tt) => ( $macro!($args, [ fn check_param(a: &ast::Param); fn check_ident(a: &ast::Ident); fn check_crate(a: &ast::Crate); fn check_crate_post(a: &ast::Crate); ... ]); ) } declare_early_lint_pass:生成trait EarlyLintPass 并调用宏 expand_early_lint_pass_methods。 macro_rules! declare_early_lint_pass { ([], [$($methods:tt)*]) => ( pub trait EarlyLintPass: LintPass { expand_early_lint_pass_methods!(&EarlyContext<'_>, [$($methods)*]); } ) } expand_early_lint_pass_methods:为check_*方法提供默认实现,即空检查。 macro_rules! expand_early_lint_pass_methods { ($context:ty, [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => ( $(#[inline(always)] fn $name(&mut self, _: $context, $(_: $arg),*) {})* ) } 这样的设计好处有以下几点: 因为 LintPass 是一个 trait,每一个 LintPass 的定义都需要实现其内部定义的所有方法。但 early lint 和 late lint 发生在编译的不同阶段,函数入参也不一致(AST 和 HIR)。因此,LintPass 的定义只包含了 fn name() 和 fn get_lints() 这两个通用的方法。而执行检查函数则定义在了更为具体的 EarlyLintPass 和 LateLintPass 中。 同样的,对于 EarlyLintPass, 每一个 lintpass 的定义都必须实现其中的所有方法。但并非每一个 lintpass 都需要检查 AST 的所有节点。 expand_early_lint_pass_methods 为其内部方法提供了默认实现。这样在定义具体的 lintpass 时,只需要关注和实现其相关的检查函数即可。例如,对于 WhileTrue 的定义,因为 while true { }这样的写法只会出现在 ast::Expr 节点中,因此只需要实现 check_expr 函数即可。在其他任何节点调用 WhileTrue 的检查函数,如在检查 AST 上的标识符节点时,调用 WhileTrue.check_ident(),则根据宏 expand_early_lint_pass_methods 中的定义执行一个空函数。 pass 的含义 在 Rustc 中,除了 Lint 和 LintPass 外,还有一些 *Pass 的命名,如 Mir 和 MirPass、rustc_passes 包等。编译原理龙书中对Pass有对应的解释: 1.2.8 将多个步骤组合成趟 前面关于步骤的讨论讲的是一个编译器的逻辑组织方式。在一个特定的实现中,多个步骤的活动可以被组合成一趟(pass)。每趟读入一个输入文件并产生一个输出文件。 在声明 LintPass 的宏 declare_lint_pass 中,其第二个参数为一个列表,表示一个 lintpass 可以生成多个 lint。Rustc 中还有一些 CombinedLintPass 中也是将所有 builtin 的 lint 汇总到一个 lintpass 中。这与龙书中“趟”的定义基本一致:LintPass 可以组合多个 Lint 的检查,每个 LintPass 读取一个 AST 并产生对应的结果。 Lint 的简单实现 在 LintPass 的定义中,给每一个 lintpass 的所有 check_* 方法都提供了一个默认实现。到这里为止,基本上已经可以实现 Lint 检查的功能。 struct Linter { } impl ast_visit::Visitor for Linter { fn visit_crate(a: ast:crate){ for lintpass in lintpasses{ lintpass.check_crate(a) } walk_crate(); } fn visit_stmt(a: ast:stmt){ for lintpass in lintpasses{ lintpass.check_stmt(a) } walk_stmt(); } ... } let linter = Linter::new(); for c in crates{ linter.visit_crate(c); } Visitor 是遍历 AST 的工具,在这里为 Linter 实现其中的 visit_* 方法,在遍历时调用所有 lintpass 的 check_* 函数。walk_* 会继续调用其他的 visit_* 函数,遍历其中的子节点。因此,对于每一个 crate, 只需要调用 visit_crate() 函数就可以遍历 AST 并完成检查。 总结 本文简单介绍了 Rustc 源码中关于 Lint 的几个重要结构。并以 WhileTrue 为例说明了 Rustc 如何中定义和实现一个 Lint,最后基于这些结构,提供了一个简易的 Lint 检查的实现方式。希望能够对理解 Rustc 及 Lint 有所帮助,如有错误,欢迎指正。KCL 的 Lint 工具也参考了其中部分设计, 由文末简易的 Linter 结构改进而成。篇幅限制,将后续的文章将继续介绍 Rustc 中 Lint 在编译过程中的注册和执行过程,如何继续优化上述 Linter 的实现,以及 KCL Lint 的设计和实践,期待继续关注。 参考链接 KusionStack: https://github.com/KusionStack Rustc: https://github.com/rust-lang/rust rustc-dev-guide: https://rustc-dev-guide.rust-lang.org/ Rust Visitor: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/index.html Rust Clippy: https://github.com/rust-lang/rust-clippy

优秀的个人博客,低调大师

处理一次系统假死工作纪实

本文转载自微信公众号「相遇Linux」,作者JeffXie 。转载本文请联系相遇Linux公众号。 最近碰到客户反馈一个问题,系统hang了,ssh登录不上,但是可以ping通,通过串口登录进去之后,敲有些命令会卡住,查看cpu负载内存都很正常,手动触发kdump之后看不出死锁/softlockup/hardlockup等异常状态.而且重要的是已经影响到客户几十亿的业务了。 我能拿到的只有客户提供的vmcore,为了生活而奔波的人儿赶紧分析起来: 系统(SUSE 11SP1) #crash ./vmcore ./vmlinux-2.6.32.59-0.19-default ./vmlinux-2.6.32.59-0.19-default.gz crash>foreach IN bt > inbt 任意找到一个IN(可中断睡眠)的进程 12813 查看堆栈: crash>bt12813 PID:12813TASK:ffff880262eda140CPU:8COMMAND:"sshd" #0[ffff880f98e1ba58]scheduleatffffffff8139d2a4 #1[ffff880f98e1bb10]schedule_timeoutatffffffff8139d935 #2[ffff880f98e1bba0]unix_wait_for_peeratffffffff81376e27 #3[ffff880f98e1bc00]unix_dgram_sendmsgatffffffff813785c2 #4[ffff880f98e1bcb0]sock_sendmsgatffffffff812e8ddc #5[ffff880f98e1be50]sys_sendtoatffffffff812e9318 #6[ffff880f98e1bf80]system_call_fastpathatffffffff81002f7b 汇编unix_dgram_sendmsg() crash>disunix_dgram_sendmsg ... 2410xffffffff813785b7<unix_dgram_sendmsg+1143>:mov%r13,%rsi 2420xffffffff813785ba<unix_dgram_sendmsg+1146>:mov%rbp,%rdi(key1) rdi代表unix_wait_for_peer函数的第一个参数 2430xffffffff813785bd<unix_dgram_sendmsg+1149>:callq0xffffffff81376da0<unix_wait_for_peer> 进程12813 unix_wait_for_peer() 函数的堆栈数据: crash>rd-SSffff880f98e1bba0-effff880f98e1bc00 ffff880f98e1bba0:unix_wait_for_peer+1350000000000000001 ffff880f98e1bbb0:[ffff880262eda140:task_struct]autoremove_wake_function ffff880f98e1bbc0:ffff88021ea8bbc0ffff880238315bc0 ffff880f98e1bbd0:memcpy_fromiovec+87[ffff880238514980:UNIX] ffff880f98e1bbe0:[ffff880238514c30:UNIX][ffff880238514980:UNIX] ffff880f98e1bbf0:[ffff880ff708b630:UNIX]7fffffffffffffff 汇编unix_wait_for_peer() crash>disunix_wait_for_peer 0xffffffff81376da0<unix_wait_for_peer>:sub$0x58,%rsp 0xffffffff81376da4<unix_wait_for_peer+4>:mov$0x1,%edx 0xffffffff81376da9<unix_wait_for_peer+9>:mov%r13,0x50(%rsp) 0xffffffff81376dae<unix_wait_for_peer+14>:lea0x2b8(%rdi),%r13 0xffffffff81376db5<unix_wait_for_peer+21>:mov%rbx,0x38(%rsp) 0xffffffff81376dba<unix_wait_for_peer+26>:mov%gs:0xa580,%rax 0xffffffff81376dc3<unix_wait_for_peer+35>:mov%rax,0x8(%rsp) 0xffffffff81376dc8<unix_wait_for_peer+40>:lea0x18(%rsp),%rax 0xffffffff81376dcd<unix_wait_for_peer+45>:mov%rbp,0x40(%rsp)(key2) rbp压入堆栈(0x58-0x40)处,正好是unix_wait_for_peer堆栈中ffff880f98e1bbe8处,即( [ffff880238514980:UNIX]) 0xffffffff81376dd2<unix_wait_for_peer+50>:mov%rdi,%rbx 0xffffffff81376dd5<unix_wait_for_peer+53>:mov%rsi,%rbp 0xffffffff81376dd8<unix_wait_for_peer+56>:mov%r13,%rdi 查看unix_wait_for_peer()函数源码: 1005staticlongunix_wait_for_peer(structsock*other,longtimeo)(key3) 1006{ 1007structunix_sock*u=unix_sk(other); 1008intsched; 1009DEFINE_WAIT(wait); 1011prepare_to_wait_exclusive(&u->peer_wait,&wait,TASK_INTERRUPTIBLE); .. 1019if(sched) 1020timeo=schedule_timeout(timeo); 1021 1022finish_wait(&u->peer_wait,&wait); 1023returntimeo; 1024} 根据s(key1)(key2)(key3)可以知道12813进程在等待unix_sock(ffff880238514980) crash>structunix_sockffff880238514980|greppeer_wait-A10 peer_wait={ lock={ raw_lock={ slock=3369322707 } }, task_list={ next=0xffff880173427bc0, prev=0xffff8801877a7bc0 } } 查询出这个unix_sock是系统中的/dev/log文件(由syslog-ng创建,系统中大量需要记录log的进程需要通过这个unix_sock与syslog-ng通信,可以参考mansyslog-ng 和/etc/syslog-ng/syslog-ng.conf 配置文件) crash>structunix_sockffff880238514980 ... dentry=0xffff880e7634b9c0, ... crash>structdentry0xffff880e7634b9c0 ... name=0xffff880e7634ba60"log" ... 遍历这个unix_sock的所有等待队列, 说明有645个进程正在等待这个unix_sock(ffff880238514980/dev/log) crash>list__wait_queue.task_list-s__wait_queue.private-H0xffff880173427bc0|grep-iprivate|wc-l 645 所有等待unix_sock的进程重定向到一个wait_unix_sock_process文件 crash>list__wait_queue.task_list-s__wait_queue.private-H0xffff880173427bc0|grep-iprivate>wait_unix_sock_process 查询所有的sshd进程 crash>ps|grepsshd 709716ffff880244cae5c0IN0.0540881392sshd 1281370978ffff880262eda140IN0.0601482900sshd 1905570972ffff880ff74c4200IN0.0601482900sshd 2164270971ffff880262fb4340IN0.0601482904sshd 2195870971ffff880ffab501c0IN0.0601482900sshd 2245970976ffff880ff1ed4100IN0.012105210272sshd 22476224590ffff88022d11a500IN0.01213524552sshd 2333470972ffff880174b04640IN0.0601482900sshd sshd进程大部分都在wait_unix_sock_process文件中,所以ssh登录之后会卡住 查看syslog-ng进程堆栈 crash>bt15232 PID:15232TASK:ffff8801aba90680CPU:6COMMAND:"syslog-ng" #0[ffff88021ea8ba58]scheduleatffffffff8139d2a4 #1[ffff88021ea8bb10]schedule_timeoutatffffffff8139d935 #2[ffff88021ea8bba0]unix_wait_for_peeratffffffff81376e27 #3[ffff88021ea8bc00]unix_dgram_sendmsgatffffffff813785c2 #4[ffff88021ea8bcb0]sock_sendmsgatffffffff812e8ddc #5[ffff88021ea8be50]sys_sendtoatffffffff812e9318 #6[ffff88021ea8bf80]system_call_fastpathatffffffff81002f7b 查看 unix_wait_for_peer()堆栈数据: crash>rd-SSffff88021ea8bba0-effff88021ea8bc00 ffff88021ea8bba0:unix_wait_for_peer+1350000000000000001 ffff88021ea8bbb0:[ffff8801aba90680:task_struct]autoremove_wake_function ffff88021ea8bbc0:ffff880fce8dfbc0ffff880f98e1bbc0 ffff88021ea8bbd0:memcpy_fromiovec+87[ffff880238514980:UNIX] ffff88021ea8bbe0:[ffff880238514c30:UNIX][ffff880238514980:UNIX](key4) ffff88021ea8bbf0:[ffff88104c1c3930:UNIX]7fffffffffffffff 由(key4)可知syslog-ng也是在等待ffff880238514980 unix_sock 所以系统中大量的进程,包括sshd cron等其他的645个进程都在等待unix_sock(/dev/log)(/dev/log由syslog-ng创建),syslog-ng卡住在等这个unix_sock之后,系统中大量的进程都会卡住,包括sshd cron进程等. 但是syslog-ng为什么卡住从vmcore根本看不出来,很可能进程在用户层锁住了. 通过查看客户系统中最近安装的软件发现,他们最近安装了两个定制软件,卸载之后系统恢复正常。 #/bin/rpm-qa--last CARKpam-11.01-1.19TueJun922:49:222020 CARKaim-11.01-1.5TueJun922:38:482020

优秀的个人博客,低调大师

Spring框架里注解@Autowired的工作原理

Suppose I have a bean named HelloWorld which has a member attribute points to another bean User. With annotation @Autowired, as long as getBean is called in the runtime, the returned HelloWorld instance will automatically have user attribute injected with User instance. How is this behavior implemented by Spring framework? (1) in Spring container implementation’s refresh method, all singleton beans will be initialized by default. When the HelloWorld bean is initialized: Since it has the following source code: @Autowired private User user; In the runtime, this annotation is available in metadata via reflection. In metadata structure below, the targetClass points to HelloWorld bean, and injectedElements points to the User class to be injected. (2) In doResolveDependency, the definition for User bean is searched based on this.beanDefinitionNames ( list in DefaultListableBeanFactory ): Once found, the found result is added to array candidateNames: Then the constructor of User bean class is called ( still triggered by getBean call ), the user instance is created by calling constructor: The created user instance together with its name “user” is inserted to the map matchingBeans. Finally the user reference is set to user attribute of HelloWorld instance via reflection. Here the variable bean in line 569 points to HelloWorld instance, and value points to user instance. Once field.set(bean, value) is done, we can observe in debugger that the user attribute in HelloWorld instance is already injected successfully. 本文来自云栖社区合作伙伴“汪子熙”,了解相关信息可以关注微信公众号"汪子熙"。

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册