debugger.rs (15502B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 use crate::{DebugCommand, RenderApi, ApiMsg}; 6 use crate::profiler::{Profiler, RenderCommandLog}; 7 use crate::composite::CompositeState; 8 use std::collections::HashMap; 9 use std::convert::Infallible; 10 use api::crossbeam_channel; 11 use api::channel::{Sender, unbounded_channel}; 12 use api::{DebugFlags, TextureCacheCategory}; 13 use api::debugger::{DebuggerMessage, SetDebugFlagsMessage, ProfileCounterDescriptor}; 14 use api::debugger::{FrameLogMessage, InitProfileCountersMessage, ProfileCounterId}; 15 use api::debugger::{CompositorDebugInfo, CompositorDebugTile}; 16 use std::thread; 17 use base64::prelude::*; 18 use sha1::{Sha1, Digest}; 19 use hyper::{Request, Response, Body, service::{make_service_fn, service_fn}, Server}; 20 use tokio::io::AsyncWriteExt; 21 22 /// A minimal wrapper around RenderApi's channel that can be cloned. 23 #[derive(Clone)] 24 struct DebugRenderApi { 25 api_sender: Sender<ApiMsg>, 26 } 27 28 impl DebugRenderApi { 29 fn new(api: &RenderApi) -> Self { 30 Self { 31 api_sender: api.get_api_sender(), 32 } 33 } 34 35 fn get_debug_flags(&self) -> DebugFlags { 36 let (tx, rx) = unbounded_channel(); 37 let msg = ApiMsg::DebugCommand(DebugCommand::GetDebugFlags(tx)); 38 self.api_sender.send(msg).unwrap(); 39 rx.recv().unwrap() 40 } 41 42 fn send_debug_cmd(&self, cmd: DebugCommand) { 43 let msg = ApiMsg::DebugCommand(cmd); 44 self.api_sender.send(msg).unwrap(); 45 } 46 } 47 48 /// Implements the WR remote debugger interface, that the `wrshell` application 49 /// can connect to when the cargo feature `debugger` is enabled. There are two 50 /// communication channels available. First, a simple HTTP server that listens 51 /// for commands and can act on those and/or return query results about WR 52 /// internal state. Second, a client can optionally connect to the /debugger-socket 53 /// endpoint for real time updates. This will be upgraded to a websocket connection 54 /// allowing the WR instance to stream information to client(s) as appropriate. 55 56 /// Details about the type of debug query being requested 57 #[derive(Clone)] 58 pub enum DebugQueryKind { 59 /// Query the current spatial tree 60 SpatialTree {}, 61 /// Query the compositing config 62 CompositorConfig {}, 63 /// Query the compositing view 64 CompositorView {}, 65 /// Query the content of GPU textures 66 Textures { category: Option<TextureCacheCategory> }, 67 } 68 69 /// Details about the debug query being requested 70 #[derive(Clone)] 71 pub struct DebugQuery { 72 /// Kind of debug query (filters etc) 73 pub kind: DebugQueryKind, 74 /// Where result should be sent 75 pub result: Sender<String>, 76 } 77 78 /// A remote debugging client. These are stored with a stream that can publish 79 /// realtime events to (such as debug flag changes, profile counter updates etc). 80 pub struct DebuggerClient { 81 tx: tokio::sync::mpsc::UnboundedSender<Vec<u8>>, 82 } 83 84 impl DebuggerClient { 85 /// Send a debugger message to this client 86 fn send_msg( 87 &mut self, 88 msg: DebuggerMessage, 89 ) -> bool { 90 let data = serde_json::to_string(&msg).expect("bug"); 91 let data = construct_server_ws_frame(&data); 92 93 self.tx.send(data).is_ok() 94 } 95 } 96 97 /// The main debugger interface that exists in a WR instance 98 pub struct Debugger { 99 /// List of currently connected debug clients 100 clients: Vec<DebuggerClient>, 101 } 102 103 impl Debugger { 104 pub fn new() -> Self { 105 Debugger { 106 clients: Vec::new(), 107 } 108 } 109 110 /// Add a newly connected client 111 pub fn add_client( 112 &mut self, 113 mut client: DebuggerClient, 114 debug_flags: DebugFlags, 115 profiler: &Profiler, 116 ) { 117 // Send initial state to client 118 let msg = SetDebugFlagsMessage { 119 flags: debug_flags, 120 }; 121 if client.send_msg(DebuggerMessage::SetDebugFlags(msg)) { 122 let mut counters = Vec::new(); 123 for (id, counter) in profiler.counters().iter().enumerate() { 124 counters.push(ProfileCounterDescriptor { 125 id: ProfileCounterId(id), 126 name: counter.name.into(), 127 }); 128 } 129 let msg = InitProfileCountersMessage { 130 counters 131 }; 132 if client.send_msg(DebuggerMessage::InitProfileCounters(msg)) { 133 // Successful initial connection, add to list for per-frame updates 134 self.clients.push(client); 135 } 136 } 137 } 138 139 /// Per-frame update. Stream any important updates to connected debug clients. 140 /// On error, the client is dropped from the active connections. 141 pub fn update( 142 &mut self, 143 debug_flags: DebugFlags, 144 profiler: &Profiler, 145 command_log: &Option<RenderCommandLog>, 146 ) { 147 let mut clients_to_keep = Vec::new(); 148 149 for mut client in self.clients.drain(..) { 150 let msg = SetDebugFlagsMessage { 151 flags: debug_flags, 152 }; 153 let profile_counters = if client.send_msg(DebuggerMessage::SetDebugFlags(msg)) { 154 Some(profiler.collect_updates_for_debugger()) 155 } else { 156 None 157 }; 158 159 let render_commands = command_log.as_ref().map(|dc| { dc.get().to_vec() }); 160 161 let msg = FrameLogMessage { 162 profile_counters, 163 render_commands, 164 }; 165 166 if client.send_msg(DebuggerMessage::UpdateFrameLog(msg)) { 167 clients_to_keep.push(client); 168 } 169 } 170 171 self.clients = clients_to_keep; 172 } 173 } 174 175 /// Start the debugger thread that listens for requests from clients. 176 pub fn start(api: RenderApi) { 177 let address = "127.0.0.1:3583"; 178 179 println!("Start WebRender debugger server on http://{}", address); 180 181 let api = DebugRenderApi::new(&api); 182 183 thread::spawn(move || { 184 let runtime = match tokio::runtime::Runtime::new() { 185 Ok(rt) => rt, 186 Err(e) => { 187 println!("\tUnable to create tokio runtime for the webrender debugger: {}", e); 188 return; 189 } 190 }; 191 192 runtime.block_on(async { 193 let make_svc = make_service_fn(move |_conn| { 194 let api = api.clone(); 195 async move { 196 Ok::<_, Infallible>(service_fn(move |req| { 197 handle_request(req, api.clone()) 198 })) 199 } 200 }); 201 202 let addr = address.parse().unwrap(); 203 let server = match Server::try_bind(&addr) { 204 Ok(s) => s, 205 Err(e) => { 206 eprintln!("WebRender debugger could not bind: {addr}: {e:?}"); 207 return; 208 } 209 }; 210 211 if let Err(e) = server.serve(make_svc).await { 212 eprintln!("WebRender debugger error: {:?}", e); 213 } 214 }); 215 }); 216 } 217 218 async fn request_to_string(request: Request<Body>) -> Result<String, hyper::Error> { 219 let body_bytes = hyper::body::to_bytes(request.into_body()).await?; 220 Ok(String::from_utf8_lossy(&body_bytes).to_string()) 221 } 222 223 fn string_response<S: Into<String>>(string: S) -> Response<Body> { 224 Response::new(Body::from(string.into())) 225 } 226 227 fn status_response(status: u16) -> Response<Body> { 228 Response::builder().status(status).body(Body::from("")).unwrap() 229 } 230 231 async fn handle_request( 232 request: Request<Body>, 233 api: DebugRenderApi, 234 ) -> Result<Response<Body>, Infallible> { 235 let path = request.uri().path(); 236 let query = request.uri().query().unwrap_or(""); 237 let args: HashMap<String, String> = url::form_urlencoded::parse(query.as_bytes()) 238 .into_owned() 239 .collect(); 240 241 match path { 242 "/ping" => { 243 // Client can check if server is online and accepting connections 244 Ok(string_response("pong")) 245 } 246 "/debug-flags" => { 247 // Get or set the current debug flags 248 match request.method() { 249 &hyper::Method::GET => { 250 let debug_flags = api.get_debug_flags(); 251 let result = serde_json::to_string(&debug_flags).unwrap(); 252 Ok(string_response(result)) 253 } 254 &hyper::Method::POST => { 255 let content = request_to_string(request).await.unwrap(); 256 let flags = serde_json::from_str(&content).expect("bug"); 257 api.send_debug_cmd( 258 DebugCommand::SetFlags(flags) 259 ); 260 api.send_debug_cmd( 261 DebugCommand::GenerateFrame 262 ); 263 Ok(string_response(format!("flags = {:?}", flags))) 264 } 265 _ => { 266 Ok(status_response(403)) 267 } 268 } 269 } 270 "/render-cmd-log" => { 271 match request.method() { 272 &hyper::Method::POST => { 273 let content = request_to_string(request).await.unwrap(); 274 let enabled = serde_json::from_str(&content).expect("bug"); 275 api.send_debug_cmd( 276 DebugCommand::SetRenderCommandLog(enabled) 277 ); 278 Ok(string_response(format!("{:?}", enabled))) 279 } 280 _ => { 281 Ok(status_response(403)) 282 } 283 } 284 } 285 "/generate-frame" => { 286 // Force generate a frame-build and composite 287 api.send_debug_cmd( 288 DebugCommand::GenerateFrame 289 ); 290 Ok(status_response(200)) 291 } 292 "/query" => { 293 // Query internal state about WR. 294 let (tx, rx) = crossbeam_channel::unbounded(); 295 let kind = match args.get("type").map(|s| s.as_str()) { 296 Some("spatial-tree") => DebugQueryKind::SpatialTree {}, 297 Some("composite-view") => DebugQueryKind::CompositorView {}, 298 Some("composite-config") => DebugQueryKind::CompositorConfig {}, 299 Some("textures") => DebugQueryKind::Textures { category: None }, 300 Some("atlas-textures") => DebugQueryKind::Textures { category: Some(TextureCacheCategory::Atlas) }, 301 Some("target-textures") => DebugQueryKind::Textures { category: Some(TextureCacheCategory::RenderTarget) }, 302 Some("tile-textures") => DebugQueryKind::Textures { category: Some(TextureCacheCategory::PictureTile) }, 303 Some("standalone-textures") => DebugQueryKind::Textures { category: Some(TextureCacheCategory::Standalone) }, 304 _ => { 305 return Ok(string_response("Unknown query")); 306 } 307 }; 308 309 let query = DebugQuery { 310 result: tx, 311 kind, 312 }; 313 api.send_debug_cmd( 314 DebugCommand::Query(query) 315 ); 316 let result = match rx.recv() { 317 Ok(result) => result, 318 Err(..) => "No response received from WR".into(), 319 }; 320 Ok(string_response(result)) 321 } 322 "/debugger-socket" => { 323 // Connect to a realtime stream of events from WR. This is handled 324 // by upgrading the HTTP request to a websocket. 325 326 let upgrade_header = request.headers().get("upgrade"); 327 if upgrade_header.is_none() || upgrade_header.unwrap() != "websocket" { 328 return Ok(status_response(404)); 329 } 330 331 let key = match request.headers().get("sec-websocket-key") { 332 Some(k) => k.to_str().unwrap_or(""), 333 None => { 334 return Ok(status_response(400)); 335 } 336 }; 337 338 let accept_key = convert_ws_key(key); 339 340 tokio::spawn(async move { 341 match hyper::upgrade::on(request).await { 342 Ok(upgraded) => { 343 let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel::<Vec<u8>>(); 344 345 // Spawn a task to handle writing to the WebSocket stream 346 tokio::spawn(async move { 347 let mut stream = upgraded; 348 while let Some(data) = rx.recv().await { 349 if stream.write_all(&data).await.is_err() { 350 break; 351 } 352 if stream.flush().await.is_err() { 353 break; 354 } 355 } 356 }); 357 358 api.send_debug_cmd( 359 DebugCommand::AddDebugClient(DebuggerClient { 360 tx, 361 }) 362 ); 363 } 364 Err(e) => eprintln!("Upgrade error: {}", e), 365 } 366 }); 367 368 Ok(Response::builder() 369 .status(101) 370 .header("upgrade", "websocket") 371 .header("connection", "upgrade") 372 .header("sec-websocket-accept", accept_key) 373 .body(Body::from("")) 374 .unwrap()) 375 } 376 _ => { 377 Ok(status_response(404)) 378 } 379 } 380 } 381 382 /// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-WebSocket-Key 383 /// See https://datatracker.ietf.org/doc/html/rfc6455#section-11.3.1 384 fn convert_ws_key(input: &str) -> String { 385 let mut input = input.to_string().into_bytes(); 386 let mut bytes = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" 387 .to_string() 388 .into_bytes(); 389 input.append(&mut bytes); 390 391 let sha1 = Sha1::digest(&input); 392 BASE64_STANDARD.encode(sha1) 393 } 394 395 /// Convert a string to a websocket text frame 396 /// See https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#exchanging_data_frames 397 pub fn construct_server_ws_frame(payload: &str) -> Vec<u8> { 398 let payload_bytes = payload.as_bytes(); 399 let payload_len = payload_bytes.len(); 400 let mut frame = Vec::new(); 401 402 frame.push(0x81); 403 404 if payload_len <= 125 { 405 frame.push(payload_len as u8); 406 } else if payload_len <= 65535 { 407 frame.push(126 as u8); 408 frame.extend_from_slice(&(payload_len as u16).to_be_bytes()); 409 } else { 410 frame.push(127 as u8); 411 frame.extend_from_slice(&(payload_len as u64).to_be_bytes()); 412 } 413 414 frame.extend_from_slice(payload_bytes); 415 416 frame 417 } 418 419 impl From<&CompositeState> for CompositorDebugInfo { 420 fn from(state: &CompositeState) -> Self { 421 let tiles = state.tiles 422 .iter() 423 .map(|tile| { 424 CompositorDebugTile { 425 local_rect: tile.local_rect, 426 clip_rect: tile.device_clip_rect, 427 device_rect: state.get_device_rect( 428 &tile.local_rect, 429 tile.transform_index, 430 ), 431 z_id: tile.z_id.0, 432 } 433 }) 434 .collect(); 435 436 CompositorDebugInfo { 437 enabled_z_layers: !0, 438 tiles, 439 } 440 } 441 }