From cc5416be4534bbd2e8067322d79e61ec75b4c042 Mon Sep 17 00:00:00 2001 From: never Date: Mon, 24 Apr 2023 13:49:08 +0800 Subject: [PATCH] feat: support backtrace in runtime --- kclvm/compiler/src/codegen/llvm/context.rs | 15 ++++-- kclvm/error/src/diagnostic.rs | 5 +- kclvm/error/src/lib.rs | 60 ++++++++++++++++----- kclvm/runtime/src/_kclvm.bc | Bin 15532 -> 15532 bytes kclvm/runtime/src/_kclvm.h | 2 +- kclvm/runtime/src/_kclvm.ll | 2 +- kclvm/runtime/src/_kclvm_api_spec.rs | 6 +-- kclvm/runtime/src/api/kclvm.rs | 36 +++++++++++++ kclvm/runtime/src/context/mod.rs | 22 +++++--- kclvm/runtime/src/value/api.rs | 29 ++++++++-- 10 files changed, 139 insertions(+), 38 deletions(-) diff --git a/kclvm/compiler/src/codegen/llvm/context.rs b/kclvm/compiler/src/codegen/llvm/context.rs index e08b1f860..415df1945 100644 --- a/kclvm/compiler/src/codegen/llvm/context.rs +++ b/kclvm/compiler/src/codegen/llvm/context.rs @@ -1,5 +1,6 @@ // Copyright 2021 The KCL Authors. All rights reserved. +use core::panic; use indexmap::{IndexMap, IndexSet}; use inkwell::basic_block::BasicBlock; use inkwell::builder::Builder; @@ -418,6 +419,8 @@ impl<'ctx> ValueMethods for LLVMCodeGenContext<'ctx> { } /// Construct a function value using a native function. fn function_value(&self, function: FunctionValue<'ctx>) -> Self::Value { + let func_name = function.get_name().to_str().unwrap(); + let func_name_ptr = self.native_global_string(func_name, func_name).into(); let lambda_fn_ptr = self.builder.build_bitcast( function.as_global_value().as_pointer_value(), self.context.i64_type().ptr_type(AddressSpace::default()), @@ -425,21 +428,22 @@ impl<'ctx> ValueMethods for LLVMCodeGenContext<'ctx> { ); self.build_call( &ApiFunc::kclvm_value_Function_using_ptr.name(), - &[lambda_fn_ptr], + &[lambda_fn_ptr, func_name_ptr], ) } /// Construct a closure function value with the closure variable. fn closure_value(&self, function: FunctionValue<'ctx>, closure: Self::Value) -> Self::Value { + let func_name = function.get_name().to_str().unwrap(); + let func_name_ptr = self.native_global_string(func_name, func_name).into(); // Convert the function to a i64 pointer to store it into the function value. let fn_ptr = self.builder.build_bitcast( function.as_global_value().as_pointer_value(), self.context.i64_type().ptr_type(AddressSpace::default()), "", ); - let name = self.native_global_string("", "").into(); self.build_call( &ApiFunc::kclvm_value_Function.name(), - &[fn_ptr, closure, name], + &[fn_ptr, closure, func_name_ptr], ) } /// Construct a schema function value using native functions. @@ -1674,11 +1678,12 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { self.context.i64_type().ptr_type(AddressSpace::default()), "", ); - let name = self.native_global_string("", "").into(); + let func_name = function.get_name().to_str().unwrap(); + let func_name_ptr = self.native_global_string(func_name, func_name).into(); let none_value = self.none_value(); self.build_call( &ApiFunc::kclvm_value_Function.name(), - &[lambda_fn_ptr, none_value, name], + &[lambda_fn_ptr, none_value, func_name_ptr], ) }; Ok(value) diff --git a/kclvm/error/src/diagnostic.rs b/kclvm/error/src/diagnostic.rs index 444574d36..2ba7fdd27 100644 --- a/kclvm/error/src/diagnostic.rs +++ b/kclvm/error/src/diagnostic.rs @@ -96,13 +96,14 @@ impl From for Position { impl Diagnostic { pub fn new(level: Level, message: &str, pos: Position) -> Self { - Diagnostic::new_with_code(level, message, pos, None) + Diagnostic::new_with_code(level, message, None, pos, None) } /// New a diagnostic with error code. pub fn new_with_code( level: Level, message: &str, + note: Option<&str>, pos: Position, code: Option, ) -> Self { @@ -112,7 +113,7 @@ impl Diagnostic { pos, style: Style::LineAndColumn, message: message.to_string(), - note: None, + note: note.map(|s| s.to_string()), }], code, } diff --git a/kclvm/error/src/lib.rs b/kclvm/error/src/lib.rs index bc1caf4e4..5cb88b39b 100644 --- a/kclvm/error/src/lib.rs +++ b/kclvm/error/src/lib.rs @@ -96,6 +96,7 @@ impl Handler { let diag = Diagnostic::new_with_code( Level::Error, &message, + None, pos, Some(DiagnosticId::Error(E1001.kind)), ); @@ -109,6 +110,7 @@ impl Handler { let diag = Diagnostic::new_with_code( Level::Error, msg, + None, pos, Some(DiagnosticId::Error(E2G22.kind)), ); @@ -122,6 +124,7 @@ impl Handler { let diag = Diagnostic::new_with_code( Level::Error, msg, + None, pos, Some(DiagnosticId::Error(E2L23.kind)), ); @@ -219,26 +222,54 @@ impl Handler { impl From for Diagnostic { fn from(panic_info: PanicInfo) -> Self { - let mut diag = Diagnostic::new_with_code( - Level::Error, - if panic_info.kcl_arg_msg.is_empty() { - &panic_info.message - } else { - &panic_info.kcl_arg_msg - }, - Position { - filename: panic_info.kcl_file.clone(), - line: panic_info.kcl_line as u64, - column: None, - }, - Some(DiagnosticId::Error(E3M38.kind)), - ); + let panic_msg = if panic_info.kcl_arg_msg.is_empty() { + &panic_info.message + } else { + &panic_info.kcl_arg_msg + }; + + let mut diag = if panic_info.backtrace.is_empty() { + Diagnostic::new_with_code( + Level::Error, + &panic_msg, + None, + Position { + filename: panic_info.kcl_file.clone(), + line: panic_info.kcl_line as u64, + column: None, + }, + Some(DiagnosticId::Error(E3M38.kind)), + ) + } else { + let mut backtrace_msg = "backtrace:\n".to_string(); + let mut backtrace = panic_info.backtrace.clone(); + backtrace.reverse(); + for (index, frame) in backtrace.iter().enumerate() { + backtrace_msg = format!( + "{backtrace_msg}\t{index}: {}\n\t\tat {}:{}:{}\n", + frame.func, frame.file, frame.line, frame.col + ); + } + Diagnostic::new_with_code( + Level::Error, + &panic_msg, + Some(&backtrace_msg), + Position { + filename: panic_info.kcl_file.clone(), + line: panic_info.kcl_line as u64, + column: None, + }, + Some(DiagnosticId::Error(E3M38.kind)), + ) + }; + if panic_info.kcl_config_meta_file.is_empty() { return diag; } let mut config_meta_diag = Diagnostic::new_with_code( Level::Error, &panic_info.kcl_config_meta_arg_msg, + None, Position { filename: panic_info.kcl_config_meta_file.clone(), line: panic_info.kcl_config_meta_line as u64, @@ -297,6 +328,7 @@ impl ParseError { Ok(Diagnostic::new_with_code( Level::Error, &self.to_string(), + None, loc.into(), Some(DiagnosticId::Error(ErrorKind::InvalidSyntax)), )) diff --git a/kclvm/runtime/src/_kclvm.bc b/kclvm/runtime/src/_kclvm.bc index 8022ac8d4cdfacf3f69ac8c58a3b465d65a4d05d..2e7a8d03f6ce79dd8bfbbb334235294ff90fc144 100644 GIT binary patch delta 55 zcmZ2exu$Z0IAi=qi4RPysR{~03nue0S8wiM=CqVH)YH=kqT-@t{rK$UoU&ZKoE!!S F008ol4`BcR delta 54 zcmZ2exu$Z0IHUhYi4ROHt_li^Ci5~^Z*FJivXnN^)6)l{;-X~z`0V7IvRu8K90mvg E0OB command, DONOT EDIT!!! @@ -275,8 +275,8 @@ // api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_Function(i64* %fn_ptr, %kclvm_value_ref_t* %closure, %kclvm_char_t* %external_name); // api-spec: kclvm_value_Function_using_ptr -// api-spec(c): kclvm_value_ref_t* kclvm_value_Function_using_ptr(uint64_t* fn_ptr); -// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_Function_using_ptr(i64* %fn_ptr); +// api-spec(c): kclvm_value_ref_t* kclvm_value_Function_using_ptr(uint64_t* fn_ptr, kclvm_char_t* external_name); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_Function_using_ptr(i64* %fn_ptr, %kclvm_char_t* %external_name); // api-spec: kclvm_value_schema_function // api-spec(c): kclvm_value_ref_t* kclvm_value_schema_function(uint64_t* fn_ptr, uint64_t* check_fn_ptr, kclvm_char_t* tpe); diff --git a/kclvm/runtime/src/api/kclvm.rs b/kclvm/runtime/src/api/kclvm.rs index a111a51ed..2d44db845 100644 --- a/kclvm/runtime/src/api/kclvm.rs +++ b/kclvm/runtime/src/api/kclvm.rs @@ -293,6 +293,7 @@ pub struct OptionHelp { #[derive(PartialEq, Eq, Clone, Default, Debug, Serialize, Deserialize)] pub struct PanicInfo { pub __kcl_PanicInfo__: bool, // "__kcl_PanicInfo__" + pub backtrace: Vec, pub rust_file: String, pub rust_line: i32, @@ -300,6 +301,7 @@ pub struct PanicInfo { pub kcl_pkgpath: String, pub kcl_file: String, + pub kcl_func: String, pub kcl_line: i32, pub kcl_col: i32, pub kcl_arg_msg: String, @@ -368,6 +370,7 @@ pub struct Context { pub main_pkg_path: String, pub main_pkg_files: Vec, + pub backtrace: Vec, pub imported_pkgpath: HashSet, pub app_args: HashMap, @@ -385,10 +388,43 @@ pub struct Context { pub objects: IndexSet, } +#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)] +pub struct BacktraceFrame { + pub file: String, + pub func: String, + pub col: i32, + pub line: i32, +} +impl Default for BacktraceFrame { + fn default() -> Self { + Self { + file: Default::default(), + func: "_kclvm_main".to_string(), + col: Default::default(), + line: Default::default(), + } + } +} + +impl BacktraceFrame { + pub fn from_panic_info(info: &PanicInfo) -> Self { + Self { + file: info.kcl_file.clone(), + func: info.kcl_func.clone(), + col: info.kcl_col, + line: info.kcl_line, + } + } +} + impl Context { pub fn new() -> Self { Context { instances: RefCell::new(HashMap::new()), + panic_info: PanicInfo { + kcl_func: "kclvm_main".to_string(), + ..Default::default() + }, ..Default::default() } } diff --git a/kclvm/runtime/src/context/mod.rs b/kclvm/runtime/src/context/mod.rs index 03c7af484..42a285b34 100644 --- a/kclvm/runtime/src/context/mod.rs +++ b/kclvm/runtime/src/context/mod.rs @@ -4,7 +4,7 @@ pub mod api; pub use api::*; use std::fmt; -use crate::PanicInfo; +use crate::{BacktraceFrame, PanicInfo}; #[allow(non_camel_case_types)] type kclvm_value_ref_t = crate::ValueRef; @@ -189,16 +189,24 @@ impl crate::Context { pub fn set_panic_info(&mut self, info: &std::panic::PanicInfo) { self.panic_info.__kcl_PanicInfo__ = true; - if let Some(s) = info.payload().downcast_ref::<&str>() { - self.panic_info.message = s.to_string(); + self.panic_info.message = if let Some(s) = info.payload().downcast_ref::<&str>() { + s.to_string() } else if let Some(s) = info.payload().downcast_ref::<&String>() { - self.panic_info.message = (*s).clone(); + (*s).clone() } else if let Some(s) = info.payload().downcast_ref::() { - self.panic_info.message = (*s).clone(); + (*s).clone() } else { - self.panic_info.message = "".to_string(); + "".to_string() + }; + if self.cfg.debug_mode { + self.panic_info.backtrace = self.backtrace.clone(); + self.panic_info.backtrace.push(BacktraceFrame { + file: self.panic_info.kcl_file.clone(), + func: self.panic_info.kcl_func.clone(), + col: self.panic_info.kcl_col, + line: self.panic_info.kcl_line, + }); } - if let Some(location) = info.location() { self.panic_info.rust_file = location.file().to_string(); self.panic_info.rust_line = location.line() as i32; diff --git a/kclvm/runtime/src/value/api.rs b/kclvm/runtime/src/value/api.rs index 83a5f00fa..3145e5194 100644 --- a/kclvm/runtime/src/value/api.rs +++ b/kclvm/runtime/src/value/api.rs @@ -331,8 +331,10 @@ pub unsafe extern "C" fn kclvm_value_Function( #[runtime_fn] pub unsafe extern "C" fn kclvm_value_Function_using_ptr( fn_ptr: *const u64, + external_name: *const kclvm_char_t, ) -> *mut kclvm_value_ref_t { - new_mut_ptr(ValueRef::func(fn_ptr as u64, 0, ValueRef::none(), "", "")) + let name = c2str(external_name); + new_mut_ptr(ValueRef::func(fn_ptr as u64, 0, ValueRef::none(), name, "")) } #[no_mangle] @@ -615,8 +617,13 @@ pub unsafe extern "C" fn kclvm_value_function_invoke( let fn_ptr = func.fn_ptr; let closure = &func.closure; let is_schema = !func.runtime_type.is_empty(); - let is_external = !func.external_name.is_empty(); let ctx_ref = mut_ptr_as_ref(ctx); + if ctx_ref.cfg.debug_mode { + ctx_ref + .backtrace + .push(BacktraceFrame::from_panic_info(&ctx_ref.panic_info)); + ctx_ref.panic_info.kcl_func = func.external_name.clone(); + } let now_meta_info = ctx_ref.panic_info.clone(); unsafe { let call_fn: SchemaTypeFunc = transmute_copy(&fn_ptr); @@ -652,9 +659,6 @@ pub unsafe extern "C" fn kclvm_value_function_invoke( args_new.list_append_unpack(&closure_new); call_fn(ctx, args_new.into_raw(), kwargs) // Normal kcl function, call directly - } else if is_external { - let name = format!("{}\0", func.external_name); - kclvm_plugin_invoke(name.as_ptr() as *const i8, args, kwargs) } else { args_ref.list_append_unpack_first(closure); let args = args_ref.clone().into_raw(); @@ -665,6 +669,9 @@ pub unsafe extern "C" fn kclvm_value_function_invoke( let schema_value = ptr_as_ref(value); schema_value.schema_check_attr_optional(true); } + if ctx_ref.cfg.debug_mode { + ctx_ref.backtrace.pop(); + } ctx_ref.panic_info = now_meta_info; return value; }; @@ -2263,6 +2270,14 @@ pub unsafe extern "C" fn kclvm_schema_value_new( if schema_value_or_func.is_func() { let schema_func = schema_value_or_func.as_function(); let schema_fn_ptr = schema_func.fn_ptr; + let ctx_ref = mut_ptr_as_ref(ctx); + let now_meta_info = ctx_ref.panic_info.clone(); + if ctx_ref.cfg.debug_mode { + ctx_ref + .backtrace + .push(BacktraceFrame::from_panic_info(&ctx_ref.panic_info)); + ctx_ref.panic_info.kcl_func = schema_func.runtime_type.clone(); + } let value = unsafe { let org_args = ptr_as_ref(args).deep_copy(); let schema_fn: SchemaTypeFunc = transmute_copy(&schema_fn_ptr); @@ -2324,6 +2339,10 @@ pub unsafe extern "C" fn kclvm_schema_value_new( } schema_fn(ctx, args, kwargs) }; + ctx_ref.panic_info = now_meta_info; + if ctx_ref.cfg.debug_mode { + ctx_ref.backtrace.pop(); + } value } else { let config = ptr_as_ref(config);