Fixed panic handling (#1667)

This commit is contained in:
Arkadiy Paronyan
2019-02-04 09:11:29 +01:00
committed by Bastian Köcher
parent 87f0f6fd8f
commit 6c341f4e9d
11 changed files with 94 additions and 18 deletions
+11 -1
View File
@@ -3433,7 +3433,6 @@ dependencies = [
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3449,6 +3448,7 @@ dependencies = [
"structopt 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"substrate-client 0.1.0", "substrate-client 0.1.0",
"substrate-network 0.1.0", "substrate-network 0.1.0",
"substrate-panic-handler 0.1.0",
"substrate-primitives 0.1.0", "substrate-primitives 0.1.0",
"substrate-service 0.3.0", "substrate-service 0.3.0",
"substrate-telemetry 0.3.0", "substrate-telemetry 0.3.0",
@@ -3618,6 +3618,7 @@ dependencies = [
"serde_derive 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-io 0.1.0", "sr-io 0.1.0",
"sr-version 0.1.0", "sr-version 0.1.0",
"substrate-panic-handler 0.1.0",
"substrate-primitives 0.1.0", "substrate-primitives 0.1.0",
"substrate-serializer 0.1.0", "substrate-serializer 0.1.0",
"substrate-state-machine 0.1.0", "substrate-state-machine 0.1.0",
@@ -3749,6 +3750,14 @@ dependencies = [
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "substrate-panic-handler"
version = "0.1.0"
dependencies = [
"backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "substrate-primitives" name = "substrate-primitives"
version = "0.1.0" version = "0.1.0"
@@ -3898,6 +3907,7 @@ dependencies = [
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"substrate-panic-handler 0.1.0",
"substrate-primitives 0.1.0", "substrate-primitives 0.1.0",
"substrate-trie 0.4.0", "substrate-trie 0.4.0",
"trie-db 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "trie-db 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
+1
View File
@@ -30,6 +30,7 @@ members = [
"core/finality-grandpa/primitives", "core/finality-grandpa/primitives",
"core/keyring", "core/keyring",
"core/network", "core/network",
"core/panic-handler",
"core/primitives", "core/primitives",
"core/rpc", "core/rpc",
"core/rpc-servers", "core/rpc-servers",
+1 -1
View File
@@ -7,7 +7,6 @@ edition = "2018"
[dependencies] [dependencies]
clap = "~2.32" clap = "~2.32"
backtrace = "0.3"
env_logger = "0.6" env_logger = "0.6"
error-chain = "0.12" error-chain = "0.12"
log = "0.4" log = "0.4"
@@ -23,6 +22,7 @@ futures = "0.1.17"
fdlimit = "0.1" fdlimit = "0.1"
exit-future = "0.1" exit-future = "0.1"
sysinfo = "0.7" sysinfo = "0.7"
panic-handler = { package = "substrate-panic-handler", path = "../../core/panic-handler" }
client = { package = "substrate-client", path = "../../core/client" } client = { package = "substrate-client", path = "../../core/client" }
network = { package = "substrate-network", path = "../../core/network" } network = { package = "substrate-network", path = "../../core/network" }
runtime_primitives = { package = "sr-primitives", path = "../../core/sr-primitives" } runtime_primitives = { package = "sr-primitives", path = "../../core/sr-primitives" }
+3 -2
View File
@@ -24,7 +24,6 @@ mod traits;
mod params; mod params;
pub mod error; pub mod error;
pub mod informant; pub mod informant;
mod panic_hook;
use runtime_primitives::traits::As; use runtime_primitives::traits::As;
use service::{ use service::{
@@ -76,6 +75,8 @@ pub struct VersionInfo {
pub description: &'static str, pub description: &'static str,
/// Executable file author. /// Executable file author.
pub author: &'static str, pub author: &'static str,
/// Support URL.
pub support_url: &'static str,
} }
/// Something that can be converted into an exit signal. /// Something that can be converted into an exit signal.
@@ -189,7 +190,7 @@ where
I: IntoIterator<Item = T>, I: IntoIterator<Item = T>,
T: Into<std::ffi::OsString> + Clone, T: Into<std::ffi::OsString> + Clone,
{ {
panic_hook::set(); panic_handler::set(version.support_url);
let full_version = service::config::full_version_from_strs( let full_version = service::config::full_version_from_strs(
version.version, version.version,
+1
View File
@@ -13,6 +13,7 @@ trie = { package = "substrate-trie", path = "../trie" }
serializer = { package = "substrate-serializer", path = "../serializer" } serializer = { package = "substrate-serializer", path = "../serializer" }
state_machine = { package = "substrate-state-machine", path = "../state-machine" } state_machine = { package = "substrate-state-machine", path = "../state-machine" }
runtime_version = { package = "sr-version", path = "../sr-version" } runtime_version = { package = "sr-version", path = "../sr-version" }
panic-handler = { package = "substrate-panic-handler", path = "../panic-handler" }
serde = "1.0" serde = "1.0"
serde_derive = "1.0" serde_derive = "1.0"
wasmi = { version = "0.4.3" } wasmi = { version = "0.4.3" }
@@ -97,11 +97,9 @@ fn fetch_cached_runtime_version<'a, E: Externalities<Blake2Hasher>>(
fn safe_call<F, U>(f: F) -> Result<U> fn safe_call<F, U>(f: F) -> Result<U>
where F: UnwindSafe + FnOnce() -> U where F: UnwindSafe + FnOnce() -> U
{ {
// Substrate uses custom panic hook that terminates process on panic. Disable it for the native call. // Substrate uses custom panic hook that terminates process on panic. Disable termination for the native call.
let hook = ::std::panic::take_hook(); let _guard = panic_handler::AbortGuard::new(false);
let result = ::std::panic::catch_unwind(f).map_err(|_| ErrorKind::Runtime.into()); ::std::panic::catch_unwind(f).map_err(|_| ErrorKind::Runtime.into())
::std::panic::set_hook(hook);
result
} }
/// Set up the externalities and safe calling environment to execute calls to a native runtime. /// Set up the externalities and safe calling environment to execute calls to a native runtime.
+10
View File
@@ -0,0 +1,10 @@
[package]
name = "substrate-panic-handler"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
description = "Substrate panic handler."
edition = "2018"
[dependencies]
backtrace = "0.3"
log = "0.4"
@@ -19,20 +19,51 @@
use backtrace::Backtrace; use backtrace::Backtrace;
use std::io::{self, Write}; use std::io::{self, Write};
use std::panic::{self, PanicInfo}; use std::panic::{self, PanicInfo};
use std::cell::Cell;
use std::thread; use std::thread;
/// Set the panic hook thread_local! {
pub fn set() { pub static ABORT: Cell<bool> = Cell::new(true);
panic::set_hook(Box::new(panic_hook));
} }
static ABOUT_PANIC: &str = " /// Set the panic hook
pub fn set(bug_url: &'static str) {
panic::set_hook(Box::new(move |c| panic_hook(c, bug_url)));
}
macro_rules! ABOUT_PANIC {
() => ("
This is a bug. Please report it at: This is a bug. Please report it at:
https://github.com/paritytech/polkadot/issues/new {}
"; ")}
fn panic_hook(info: &PanicInfo) { /// Set aborting flag. Returns previous value of the flag.
pub fn set_abort(enabled: bool) -> bool {
ABORT.with(|flag| {
let prev = flag.get();
flag.set(enabled);
prev
})
}
/// Abort flag guard. Sets abort on construction and reverts to previous setting when dropped.
pub struct AbortGuard(bool);
impl AbortGuard {
/// Create a new guard and set abort flag to specified value.
pub fn new(enable: bool) -> AbortGuard {
AbortGuard(set_abort(enable))
}
}
impl Drop for AbortGuard {
fn drop(&mut self) {
set_abort(self.0);
}
}
fn panic_hook(info: &PanicInfo, report_url: &'static str) {
let location = info.location(); let location = info.location();
let file = location.as_ref().map(|l| l.file()).unwrap_or("<unknown>"); let file = location.as_ref().map(|l| l.file()).unwrap_or("<unknown>");
let line = location.as_ref().map(|l| l.line()).unwrap_or(0); let line = location.as_ref().map(|l| l.line()).unwrap_or(0);
@@ -63,7 +94,17 @@ fn panic_hook(info: &PanicInfo) {
name, msg, file, line name, msg, file, line
); );
let _ = writeln!(stderr, "{}", ABOUT_PANIC); let _ = writeln!(stderr, ABOUT_PANIC!(), report_url);
::std::process::exit(1); ABORT.with(|flag| {
if flag.get() {
::std::process::exit(1);
}
})
} }
#[test]
fn does_not_abort() {
set("test");
let _guard = AbortGuard::new(false);
::std::panic::catch_unwind(|| panic!()).ok();
}
+1
View File
@@ -15,4 +15,5 @@ trie-db = "0.9"
trie-root = "0.9" trie-root = "0.9"
trie = { package = "substrate-trie", path = "../trie" } trie = { package = "substrate-trie", path = "../trie" }
primitives = { package = "substrate-primitives", path = "../primitives" } primitives = { package = "substrate-primitives", path = "../primitives" }
panic-handler = { package = "substrate-panic-handler", path = "../panic-handler" }
parity-codec = "3.0" parity-codec = "3.0"
+12
View File
@@ -181,21 +181,25 @@ where
H::Out: Ord + HeapSizeOf, H::Out: Ord + HeapSizeOf,
{ {
fn storage(&self, key: &[u8]) -> Option<Vec<u8>> { fn storage(&self, key: &[u8]) -> Option<Vec<u8>> {
let _guard = panic_handler::AbortGuard::new(true);
self.overlay.storage(key).map(|x| x.map(|x| x.to_vec())).unwrap_or_else(|| self.overlay.storage(key).map(|x| x.map(|x| x.to_vec())).unwrap_or_else(||
self.backend.storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL)) self.backend.storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL))
} }
fn storage_hash(&self, key: &[u8]) -> Option<H::Out> { fn storage_hash(&self, key: &[u8]) -> Option<H::Out> {
let _guard = panic_handler::AbortGuard::new(true);
self.overlay.storage(key).map(|x| x.map(|x| H::hash(x))).unwrap_or_else(|| self.overlay.storage(key).map(|x| x.map(|x| H::hash(x))).unwrap_or_else(||
self.backend.storage_hash(key).expect(EXT_NOT_ALLOWED_TO_FAIL)) self.backend.storage_hash(key).expect(EXT_NOT_ALLOWED_TO_FAIL))
} }
fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Option<Vec<u8>> { fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Option<Vec<u8>> {
let _guard = panic_handler::AbortGuard::new(true);
self.overlay.child_storage(storage_key, key).map(|x| x.map(|x| x.to_vec())).unwrap_or_else(|| self.overlay.child_storage(storage_key, key).map(|x| x.map(|x| x.to_vec())).unwrap_or_else(||
self.backend.child_storage(storage_key, key).expect(EXT_NOT_ALLOWED_TO_FAIL)) self.backend.child_storage(storage_key, key).expect(EXT_NOT_ALLOWED_TO_FAIL))
} }
fn exists_storage(&self, key: &[u8]) -> bool { fn exists_storage(&self, key: &[u8]) -> bool {
let _guard = panic_handler::AbortGuard::new(true);
match self.overlay.storage(key) { match self.overlay.storage(key) {
Some(x) => x.is_some(), Some(x) => x.is_some(),
_ => self.backend.exists_storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL), _ => self.backend.exists_storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL),
@@ -203,6 +207,7 @@ where
} }
fn exists_child_storage(&self, storage_key: &[u8], key: &[u8]) -> bool { fn exists_child_storage(&self, storage_key: &[u8], key: &[u8]) -> bool {
let _guard = panic_handler::AbortGuard::new(true);
match self.overlay.child_storage(storage_key, key) { match self.overlay.child_storage(storage_key, key) {
Some(x) => x.is_some(), Some(x) => x.is_some(),
_ => self.backend.exists_child_storage(storage_key, key).expect(EXT_NOT_ALLOWED_TO_FAIL), _ => self.backend.exists_child_storage(storage_key, key).expect(EXT_NOT_ALLOWED_TO_FAIL),
@@ -210,6 +215,7 @@ where
} }
fn place_storage(&mut self, key: Vec<u8>, value: Option<Vec<u8>>) { fn place_storage(&mut self, key: Vec<u8>, value: Option<Vec<u8>>) {
let _guard = panic_handler::AbortGuard::new(true);
if is_child_storage_key(&key) { if is_child_storage_key(&key) {
warn!(target: "trie", "Refuse to directly set child storage key"); warn!(target: "trie", "Refuse to directly set child storage key");
return; return;
@@ -220,6 +226,7 @@ where
} }
fn place_child_storage(&mut self, storage_key: Vec<u8>, key: Vec<u8>, value: Option<Vec<u8>>) -> bool { fn place_child_storage(&mut self, storage_key: Vec<u8>, key: Vec<u8>, value: Option<Vec<u8>>) -> bool {
let _guard = panic_handler::AbortGuard::new(true);
if !is_child_storage_key(&storage_key) || !is_child_trie_key_valid::<H>(&storage_key) { if !is_child_storage_key(&storage_key) || !is_child_trie_key_valid::<H>(&storage_key) {
return false; return false;
} }
@@ -231,6 +238,7 @@ where
} }
fn kill_child_storage(&mut self, storage_key: &[u8]) { fn kill_child_storage(&mut self, storage_key: &[u8]) {
let _guard = panic_handler::AbortGuard::new(true);
if !is_child_storage_key(storage_key) || !is_child_trie_key_valid::<H>(storage_key) { if !is_child_storage_key(storage_key) || !is_child_trie_key_valid::<H>(storage_key) {
return; return;
} }
@@ -243,6 +251,7 @@ where
} }
fn clear_prefix(&mut self, prefix: &[u8]) { fn clear_prefix(&mut self, prefix: &[u8]) {
let _guard = panic_handler::AbortGuard::new(true);
if is_child_storage_key(prefix) { if is_child_storage_key(prefix) {
warn!(target: "trie", "Refuse to directly clear prefix that is part of child storage key"); warn!(target: "trie", "Refuse to directly clear prefix that is part of child storage key");
return; return;
@@ -260,6 +269,7 @@ where
} }
fn storage_root(&mut self) -> H::Out { fn storage_root(&mut self) -> H::Out {
let _guard = panic_handler::AbortGuard::new(true);
if let Some((_, ref root)) = self.storage_transaction { if let Some((_, ref root)) = self.storage_transaction {
return root.clone(); return root.clone();
} }
@@ -283,6 +293,7 @@ where
} }
fn child_storage_root(&mut self, storage_key: &[u8]) -> Option<Vec<u8>> { fn child_storage_root(&mut self, storage_key: &[u8]) -> Option<Vec<u8>> {
let _guard = panic_handler::AbortGuard::new(true);
if !is_child_storage_key(storage_key) || !is_child_trie_key_valid::<H>(storage_key) { if !is_child_storage_key(storage_key) || !is_child_trie_key_valid::<H>(storage_key) {
return None; return None;
} }
@@ -295,6 +306,7 @@ where
} }
fn storage_changes_root(&mut self, parent: H::Out, parent_num: u64) -> Option<H::Out> { fn storage_changes_root(&mut self, parent: H::Out, parent_num: u64) -> Option<H::Out> {
let _guard = panic_handler::AbortGuard::new(true);
let root_and_tx = compute_changes_trie_root::<_, T, H>( let root_and_tx = compute_changes_trie_root::<_, T, H>(
self.backend, self.backend,
self.changes_trie_storage.clone(), self.changes_trie_storage.clone(),
+1
View File
@@ -60,6 +60,7 @@ fn run() -> cli::error::Result<()> {
executable_name: "substrate", executable_name: "substrate",
author: "Parity Technologies <admin@parity.io>", author: "Parity Technologies <admin@parity.io>",
description: "Generic substrate node", description: "Generic substrate node",
support_url: "https://github.com/paritytech/substrate/issues/new",
}; };
cli::run(::std::env::args(), Exit, version) cli::run(::std::env::args(), Exit, version)
} }