1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. use std::ffi::{CStr, CString}; use std::fmt; use std::ptr; use super::error::SBError; use super::platform::SBPlatform; use super::stream::SBStream; use super::target::SBTarget; use sys; /// Creates [`SBTarget`]s, provides access to them and manages /// the overall debugging experience. /// /// # Initialization and Teardown /// /// LLDB must be initialized before the functionality is used. This /// is done with `SBDebugger::initialize()`: /// /// ``` /// use lldb::SBDebugger; /// /// SBDebugger::initialize(); /// ``` /// /// Similarly, it must be terminated after you are done using it: /// /// ``` /// use lldb::SBDebugger; /// /// SBDebugger::initialize(); /// // Use LLDB functionality ... /// SBDebugger::terminate(); /// ``` /// /// Once you've initialized LLDB, you're ready to create an instance /// of `SBDebugger`: /// /// ``` /// use lldb::SBDebugger; /// /// SBDebugger::initialize(); /// /// let debugger = SBDebugger::create(false); /// // ... configure the debugger if needed ... /// // ... create a target and do stuff ... /// /// SBDebugger::terminate(); /// ``` /// /// # Configuration /// /// ## Async Mode /// /// While it is best to use LLDB in asynchronous mode, it does offer a /// synchronous mode, which can be easier to use for quick experiments /// or scripts. /// /// In synchronous mode, calls to the LLDB API do not return until the /// underlying action has been completed. This means that the thread /// from which you call LLDB will be blocked during that time, so this /// is not an ideal way to use LLDB for building interactive tools /// or a new user interface. /// /// In asynchronous mode, calls to the LLDB API will return immediately /// without waiting for the action to complete. This means that actions /// like launching a target, continuing the execution of a process and /// so on won't be completed immediately and you must process events /// to see what the results of an action are. /// /// Synchronous mode can be enabled by using [`set_async`] and passing it /// a `false` value. You can see if you're in asynchronous mode or not /// by calling [`async`]. /// /// # Target Management /// /// The `SBDebugger` instance tracks the various targets that are /// currently known to the debugger. /// /// Typically, you create a target with [`create_target`], /// [`create_target_simple`] or one of the related methods. /// /// Sometimes, you'll want to create a target without an associated /// executable. A common use case for this is to attach to a process /// by name or process ID where you don't know the executable in advance. /// The most convenient way to do this is: /// /// ``` /// # use lldb::SBDebugger; /// # SBDebugger::initialize(); /// let debugger = SBDebugger::create(false); /// if let Some(target) = debugger.create_target_simple("") { /// println!("Got a target: {:?}", target); /// // Now, maybe we'd like to attach to something. /// } /// # SBDebugger::terminate(); /// ``` /// /// You can iterate over these targets which have been created by /// using [`targets`]: /// /// ```no_run /// # use lldb::{SBDebugger, SBTarget}; /// # fn look_at_targets(debugger: SBDebugger) { /// // Iterate over the targets... /// for target in debugger.targets() { /// println!("Hello {:?}!", target); /// } /// // Or collect them into a vector! /// let targets = debugger.targets().collect::<Vec<SBTarget>>(); /// # } /// ``` /// /// # Platform Management /// /// ... /// /// [`SBTarget`]: struct.SBTarget.html /// [`set_async`]: #method.set_async /// [`async`]: #method.async /// [`create_target`]: #method.create_target /// [`create_target_simple`]: #method.create_target_simple /// [`targets`]: #method.targets pub struct SBDebugger { /// The underlying raw `SBDebuggerRef`. pub raw: sys::SBDebuggerRef, } impl SBDebugger { /// Initialize LLDB. /// /// This should be called before LLDB functionality is used. pub fn initialize() { unsafe { sys::SBDebuggerInitialize() }; } /// Tear down LLDB. /// /// This should be called once the application no longer needs /// to use LLDB functionality. Typically, this is called as the /// application exits. pub fn terminate() { unsafe { sys::SBDebuggerTerminate() }; } /// Create a new instance of `SBDebugger`. /// /// If `source_init_files` is `true`, then `~/.lldbinit` will /// be processed. pub fn create(source_init_files: bool) -> SBDebugger { SBDebugger { raw: unsafe { sys::SBDebuggerCreate2(source_init_files as u8) } } } /// Get whether or not the debugger is in async mode. /// /// When in async mode, the debugger returns immediately when /// stepping or continuing without waiting for the process /// to change state. pub fn async(&self) -> bool { unsafe { sys::SBDebuggerGetAsync(self.raw) != 0 } } /// Set the debugger to be in async mode or not. /// /// When in async mode, the debugger returns immediately when /// stepping or continuing without waiting for the process /// to change state. pub fn set_async(&self, async: bool) { unsafe { sys::SBDebuggerSetAsync(self.raw, async as u8) } } /// Get the LLDB version string. pub fn version() -> String { unsafe { match CStr::from_ptr(sys::SBDebuggerGetVersionString()).to_str() { Ok(s) => s.to_owned(), _ => panic!("No version string?"), } } } /// Create a target. /// /// The executable name may be an empty string to create /// an empty target. pub fn create_target( &self, executable: &str, target_triple: Option<&str>, platform_name: Option<&str>, add_dependent_modules: bool, ) -> Result<SBTarget, SBError> { let executable = CString::new(executable).unwrap(); let target_triple = target_triple.map(|s| CString::new(s).unwrap()); let platform_name = platform_name.map(|s| CString::new(s).unwrap()); let error = SBError::new(); let target = unsafe { sys::SBDebuggerCreateTarget( self.raw, executable.as_ptr(), target_triple.map_or(ptr::null(), |s| s.as_ptr()), platform_name.map_or(ptr::null(), |s| s.as_ptr()), add_dependent_modules as u8, error.raw, ) }; if error.is_success() { Ok(SBTarget::wrap(target)) } else { Err(error) } } /// Create a target from just an executable name. /// /// The executable name may be an empty string to create /// an empty target. /// /// Using [`create_target`] is preferred in most cases as /// that provides access to an `SBError` to inform the caller /// about what might have gone wrong. pub fn create_target_simple(&self, executable: &str) -> Option<SBTarget> { let executable = CString::new(executable).unwrap(); SBTarget::maybe_wrap(unsafe { sys::SBDebuggerCreateTarget2(self.raw, executable.as_ptr()) }) } /// Get an iterator over the [targets] known to this debugger instance. /// /// [targets]: struct.SBTarget.html pub fn targets(&self) -> SBDebuggerTargetIter { SBDebuggerTargetIter { debugger: self, idx: 0, } } /// Get the currently selected [`SBTarget`]. /// /// [SBTarget]: struct.SBTarget.html pub fn selected_target(&self) -> Option<SBTarget> { SBTarget::maybe_wrap(unsafe { sys::SBDebuggerGetSelectedTarget(self.raw) }) } /// Set the selected [`SBTarget`]. /// /// [SBTarget]: struct.SBTarget.html pub fn set_selected_target(&self, target: &SBTarget) { unsafe { sys::SBDebuggerSetSelectedTarget(self.raw, target.raw) }; } /// Get the currently selected [`SBPlatform`]. /// /// [`SBPlatform`]: struct.SBPlatform.html pub fn selected_platform(&self) -> SBPlatform { unsafe { SBPlatform { raw: sys::SBDebuggerGetSelectedPlatform(self.raw) } } } /// Set the selected [`SBPlatform`]. /// /// [`SBPlatform`]: struct.SBPlatform.html pub fn set_selected_platform(&self, platform: &SBPlatform) { unsafe { sys::SBDebuggerSetSelectedPlatform(self.raw, platform.raw) }; } } /// Iterate over the [targets] known to a [debugger]. /// /// [targets]: struct.SBTarget.html /// [debugger]: struct.SBDebugger.html pub struct SBDebuggerTargetIter<'d> { debugger: &'d SBDebugger, idx: usize, } impl<'d> Iterator for SBDebuggerTargetIter<'d> { type Item = SBTarget; fn next(&mut self) -> Option<SBTarget> { if self.idx < unsafe { sys::SBDebuggerGetNumTargets(self.debugger.raw) as usize } { let r = Some(SBTarget { raw: unsafe { sys::SBDebuggerGetTargetAtIndex(self.debugger.raw, self.idx as u32) }, }); self.idx += 1; r } else { None } } fn size_hint(&self) -> (usize, Option<usize>) { let sz = unsafe { sys::SBDebuggerGetNumTargets(self.debugger.raw) } as usize; (sz - self.idx, Some(sz)) } } impl fmt::Debug for SBDebugger { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let stream = SBStream::new(); unsafe { sys::SBDebuggerGetDescription(self.raw, stream.raw) }; write!(fmt, "SBDebugger {{ {} }}", stream.data()) } } impl Drop for SBDebugger { fn drop(&mut self) { unsafe { sys::DisposeSBDebugger(self.raw) }; } } #[cfg(feature = "graphql")] impl ::juniper::Context for SBDebugger {} #[cfg(feature = "graphql")] graphql_object!(SBDebugger: SBDebugger | &self | { field targets() -> Vec<SBTarget> { self.targets().collect() } field selected_target() -> Option<SBTarget> { self.selected_target() } field selected_platform() -> SBPlatform { self.selected_platform() } }); #[cfg(test)] mod tests { use super::SBDebugger; #[test] fn it_works() { assert!(!SBDebugger::version().is_empty()); } }