feat: shorten thread names

The Linux kernel imposes a 15-byte limit on thread names[1]. This means
information that does not fit in this limit, e.g., the pipeline ID of
layout and script threads, is lost in a debugger and profiler (see the
first column of the table below).

This commit shortens the thread names used in Servo to maximize the
amount of information conveyed. It also rectifies some inconsistencies
in the names.

|       Before      |       After       |
|-------------------|-------------------|
| `BluetoothThread` | `Bluetooth`       |
| `CanvasThread`    | `Canvas`          |
| `display alert d` | `AlertDialog`     |
| `FontCacheThread` | `FontCache`       |
| `GLPlayerThread`  | `GLPlayer`        |
| `HTML Parser`     | `Parse:www.examp` |
| `LayoutThread Pi` | `Layout(1,1)`     |
| `Memory profiler` | `MemoryProfiler`  |
| `Memory profiler` | `MemoryProfTimer` |
| `OfflineAudioCon` | `OfflineACResolv` |
| `PullTimelineMar` | `PullTimelineDat` |
| `ScriptThread Pi` | `Script(1,1)`     |
| `WebWorker for h` | `WW:www.example.` |
| `ServiceWorker f` | `SW:www.example.` |
| `ServiceWorkerMa` | `SvcWorkerManage` |
| `Time profiler t` | `TimeProfTimer`   |
| `Time profiler`   | `TimeProfiler`    |
| `WebGL thread`    | `WebGL`           |
| `Choose a device` | `DevicePicker`    |
| `Pick a file`     | `FilePicker`      |
| `Pick files`      | `FilePicker`      |

[1]: https://stackoverflow.com/questions/5026531/thread-name-longer-than-15-chars
This commit is contained in:
yvt 2021-07-19 00:50:04 +09:00
parent e8cb9f56e3
commit 41b3726271
21 changed files with 59 additions and 29 deletions

View file

@ -71,7 +71,7 @@ impl BluetoothThreadFactory for IpcSender<BluetoothRequest> {
}
.ok();
thread::Builder::new()
.name("BluetoothThread".to_owned())
.name("Bluetooth".to_owned())
.spawn(move || {
BluetoothManager::new(receiver, adapter, embedder_proxy).start();
})

View file

@ -62,7 +62,7 @@ impl<'a> CanvasPaintThread<'a> {
let msg_receiver = ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(ipc_receiver);
let (create_sender, create_receiver) = unbounded();
thread::Builder::new()
.name("CanvasThread".to_owned())
.name("Canvas".to_owned())
.spawn(move || {
let mut canvas_paint_thread = CanvasPaintThread::new(webrender_api, font_cache_thread);
loop {

View file

@ -312,7 +312,7 @@ impl WebGLThread {
/// in parallel on its own dedicated thread.
pub(crate) fn run_on_own_thread(init: WebGLThreadInit) {
thread::Builder::new()
.name("WebGL thread".to_owned())
.name("WebGL".to_owned())
.spawn(move || {
let mut data = WebGLThread::new(init);
data.process();

View file

@ -158,7 +158,7 @@ impl TimelineActor {
}
thread::Builder::new()
.name("PullTimelineMarkers".to_owned())
.name("PullTimelineData".to_owned())
.spawn(move || loop {
if !*is_recording.lock().unwrap() {
break;

View file

@ -573,7 +573,7 @@ fn run_server(
}
thread::Builder::new()
.name("DevtoolsClientAcceptor".to_owned())
.name("DevtCliAcceptor".to_owned())
.spawn(move || {
// accept connections and process them, spawning a new thread for each one
for stream in listener.incoming() {

View file

@ -444,7 +444,7 @@ impl FontCacheThread {
let channel_to_self = chan.clone();
thread::Builder::new()
.name("FontCacheThread".to_owned())
.name("FontCache".to_owned())
.spawn(move || {
// TODO: Allow users to specify these.
let generic_fonts = populate_generic_fonts();

View file

@ -285,7 +285,7 @@ impl LayoutThreadFactory for LayoutThread {
dump_flow_tree: bool,
) {
thread::Builder::new()
.name(format!("LayoutThread {:?}", id))
.name(format!("Layout{}", id))
.spawn(move || {
thread_state::initialize(ThreadState::LAYOUT);

View file

@ -253,7 +253,7 @@ impl LayoutThreadFactory for LayoutThread {
dump_flow_tree: bool,
) {
thread::Builder::new()
.name(format!("LayoutThread {:?}", id))
.name(format!("Layout{}", id))
.spawn(move || {
thread_state::initialize(ThreadState::LAYOUT);

View file

@ -34,7 +34,7 @@ impl GLPlayerThread {
) -> GLPlayerSender<GLPlayerMsg> {
let (sender, receiver) = glplayer_channel::<GLPlayerMsg>().unwrap();
thread::Builder::new()
.name("GLPlayerThread".to_owned())
.name("GLPlayer".to_owned())
.spawn(move || {
let mut renderer = GLPlayerThread::new(external_images);
loop {

View file

@ -37,7 +37,7 @@ impl Profiler {
if let Some(period) = period {
let chan = chan.clone();
thread::Builder::new()
.name("Memory profiler timer".to_owned())
.name("MemoryProfTimer".to_owned())
.spawn(move || loop {
thread::sleep(duration_from_seconds(period));
if chan.send(ProfilerMsg::Print).is_err() {
@ -50,7 +50,7 @@ impl Profiler {
// Always spawn the memory profiler. If there is no timer thread it won't receive regular
// `Print` events, but it will still receive the other events.
thread::Builder::new()
.name("Memory profiler".to_owned())
.name("MemoryProfiler".to_owned())
.spawn(move || {
let mut mem_profiler = Profiler::new(port);
mem_profiler.start();

View file

@ -174,7 +174,7 @@ impl Profiler {
// Spawn the time profiler thread
let outputoption = option.clone();
thread::Builder::new()
.name("Time profiler".to_owned())
.name("TimeProfiler".to_owned())
.spawn(move || {
let trace = file_path.as_ref().and_then(|p| TraceDump::new(p).ok());
let mut profiler = Profiler::new(port, trace, Some(outputoption));
@ -188,7 +188,7 @@ impl Profiler {
// Spawn a timer thread
let chan = chan.clone();
thread::Builder::new()
.name("Time profiler timer".to_owned())
.name("TimeProfTimer".to_owned())
.spawn(move || loop {
thread::sleep(duration_from_seconds(period));
if chan.send(ProfilerMsg::Print).is_err() {
@ -204,7 +204,7 @@ impl Profiler {
if file_path.is_some() {
// Spawn the time profiler
thread::Builder::new()
.name("Time profiler".to_owned())
.name("TimeProfiler".to_owned())
.spawn(move || {
let trace = file_path.as_ref().and_then(|p| TraceDump::new(p).ok());
let mut profiler = Profiler::new(port, trace, None);
@ -214,7 +214,7 @@ impl Profiler {
} else {
// No-op to handle messages when the time profiler is not printing:
thread::Builder::new()
.name("Time profiler".to_owned())
.name("TimeProfiler".to_owned())
.spawn(move || loop {
match port.recv() {
Err(_) => break,

View file

@ -328,7 +328,6 @@ impl DedicatedWorkerGlobalScope {
context_sender: Sender<ContextForRequestInterrupt>,
) -> JoinHandle<()> {
let serialized_worker_url = worker_url.to_string();
let name = format!("WebWorker for {}", serialized_worker_url);
let top_level_browsing_context_id = TopLevelBrowsingContextId::installed();
let current_global = GlobalScope::current().expect("No current global object");
let origin = current_global.origin().immutable().clone();
@ -337,7 +336,7 @@ impl DedicatedWorkerGlobalScope {
let current_global_https_state = current_global.get_https_state();
thread::Builder::new()
.name(name)
.name(format!("WW:{}", worker_url.debug_compact()))
.spawn(move || {
thread_state::initialize(ThreadState::SCRIPT | ThreadState::IN_WORKER);

View file

@ -153,7 +153,7 @@ impl OfflineAudioContextMethods for OfflineAudioContext {
.task_manager()
.dom_manipulation_task_source_with_canceller();
Builder::new()
.name("OfflineAudioContextResolver".to_owned())
.name("OfflineACResolver".to_owned())
.spawn(move || {
let _ = receiver.recv();
let _ = task_source.queue_with_canceller(

View file

@ -302,7 +302,7 @@ impl ServiceWorkerGlobalScope {
let serialized_worker_url = script_url.to_string();
let origin = scope_url.origin();
thread::Builder::new()
.name(format!("ServiceWorker for {}", serialized_worker_url))
.name(format!("SW:{}", script_url.debug_compact()))
.spawn(move || {
thread_state::initialize(ThreadState::SCRIPT | ThreadState::IN_WORKER);
let runtime = new_rt_and_cx(None);

View file

@ -256,7 +256,7 @@ impl Tokenizer {
// will be generated from the input provided. These parser actions are then passed
// onto the main thread to be executed.
thread::Builder::new()
.name(String::from("HTML Parser"))
.name(format!("Parse:{}", tokenizer.url.debug_compact()))
.spawn(move || {
run(
sink,

View file

@ -773,7 +773,7 @@ impl ScriptThreadFactory for ScriptThread {
let (sender, receiver) = unbounded();
let layout_chan = sender.clone();
thread::Builder::new()
.name(format!("ScriptThread {:?}", state.id))
.name(format!("Script{}", state.id))
.spawn(move || {
thread_state::initialize(ThreadState::SCRIPT);
PipelineNamespace::install(state.pipeline_namespace_id);

View file

@ -499,7 +499,7 @@ impl ServiceWorkerManagerFactory for ServiceWorkerManager {
let resource_port = ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(resource_port);
let _ = resource_sender.send(CoreResourceMsg::NetworkMediator(resource_chan, origin));
if thread::Builder::new()
.name("ServiceWorkerManager".to_owned())
.name("SvcWorkerManager".to_owned())
.spawn(move || {
ServiceWorkerManager::new(
own_sender,

View file

@ -37,7 +37,7 @@ pub struct StyleThreadPool {
}
fn thread_name(index: usize) -> String {
format!("StyleThread#{}", index)
format!("Style#{}", index)
}
// A counter so that we can wait for shutdown of all threads. See

View file

@ -179,6 +179,39 @@ impl ServoUrl {
Ok(Self::from_url(Url::from_file_path(path)?))
}
/// Return a non-standard shortened form of the URL. Mainly intended to be
/// used for debug printing in a constrained space (e.g., thread names).
pub fn debug_compact(&self) -> impl std::fmt::Display + '_ {
match self.scheme() {
"http" | "https" => {
// Strip `scheme://`, which is hardly useful for identifying websites
let mut st = self.as_str();
st = st.strip_prefix(self.scheme()).unwrap_or(st);
st = st.strip_prefix(":").unwrap_or(st);
st = st.trim_start_matches('/');
// Don't want to return an empty string
if st.is_empty() {
st = self.as_str();
}
st
},
"file" => {
// The only useful part in a `file` URL is usually only the last
// few components
let path = self.path();
let i = path.rfind('/');
let i = i.map(|i| path[..i].rfind('/').unwrap_or(i));
match i {
None | Some(0) => path,
Some(i) => &path[i + 1..],
}
},
_ => self.as_str(),
}
}
/// <https://w3c.github.io/webappsec-secure-contexts/#potentially-trustworthy-url>
pub fn is_potentially_trustworthy(&self) -> bool {
// Step 1

View file

@ -109,7 +109,7 @@ fn cookie_msg_to_cookie(cookie: cookie::Cookie) -> Cookie {
pub fn start_server(port: u16, constellation_chan: Sender<ConstellationMsg>) {
let handler = Handler::new(constellation_chan);
thread::Builder::new()
.name("WebdriverHttpServer".to_owned())
.name("WebDriverHttpServer".to_owned())
.spawn(move || {
let address = SocketAddrV4::new("0.0.0.0".parse().unwrap(), port);
match server::start(SocketAddr::V4(address), handler, extension_routes()) {

View file

@ -307,7 +307,7 @@ where
}
} else {
thread::Builder::new()
.name("display alert dialog".to_owned())
.name("AlertDialog".to_owned())
.spawn(move || match definition {
PromptDefinition::Alert(mut message, sender) => {
if origin == PromptOrigin::Untrusted {
@ -554,10 +554,8 @@ fn prompt_user(_prompt: PermissionPrompt) -> PermissionRequest {
#[cfg(target_os = "linux")]
fn platform_get_selected_devices(devices: Vec<String>) -> Option<String> {
let picker_name = "Choose a device";
thread::Builder::new()
.name(picker_name.to_owned())
.name("DevicePicker".to_owned())
.spawn(move || {
let dialog_rows: Vec<&str> = devices.iter().map(|s| s.as_ref()).collect();
let dialog_rows: Option<&[&str]> = Some(dialog_rows.as_slice());
@ -592,7 +590,7 @@ fn get_selected_files(patterns: Vec<FilterPattern>, multiple_files: bool) -> Opt
"Pick a file"
};
thread::Builder::new()
.name(picker_name.to_owned())
.name("FilePicker".to_owned())
.spawn(move || {
let mut filters = vec![];
for p in patterns {