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
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
use std::mem;
use std::fmt;
use std::thread;
use std::cell::RefCell;
use std::sync::{Arc, RwLock};
use std::borrow::Cow;

use api::protocol::{Breadcrumb, Context, User, Value};
use client::Client;
use utils;

use im;

lazy_static! {
    static ref PROCESS_STACK: RwLock<Stack> = RwLock::new(Stack::for_process());
}
thread_local! {
    static THREAD_STACK: RefCell<Stack> = RefCell::new(Stack::for_thread());
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum StackType {
    Process,
    Thread,
}

#[derive(Debug)]
pub struct Stack {
    layers: Vec<StackLayer>,
    ty: StackType,
}

#[derive(PartialEq, Clone, Copy)]
struct StackLayerToken(*const Stack, usize);

#[allow(unused)]
pub(crate) fn scope_panic_safe() -> bool {
    PROCESS_STACK.try_read().is_ok() && THREAD_STACK.with(|x| x.try_borrow().is_ok())
}

/// Holds contextual data for the current scope.
///
/// The scope is an object that can cloned efficiently and stores data that
/// is locally relevant to an event.  For instance the scope will hold recorded
/// breadcrumbs and similar information.
///
/// The scope can be interacted with in two ways:
///
/// 1. the scope is routinely updated with information by functions such as
///    `add_breadcrumb` which will modify the currently top-most scope.
/// 2. the topmost scope can also be configured through the `configure_scope`
///    method.
///
/// Note that the scope can only be modified but not inspected.  Only the
/// client can use the scope to extract information currently.
#[derive(Debug, Clone)]
pub struct Scope {
    pub(crate) fingerprint: Option<Arc<Vec<Cow<'static, str>>>>,
    pub(crate) transaction: Option<Arc<String>>,
    pub(crate) breadcrumbs: im::Vector<Breadcrumb>,
    pub(crate) user: Option<Arc<User>>,
    pub(crate) extra: im::HashMap<String, Value>,
    pub(crate) tags: im::HashMap<String, String>,
    pub(crate) contexts: im::HashMap<String, Context>,
}

fn default_scope() -> Scope {
    Scope {
        fingerprint: None,
        transaction: None,
        breadcrumbs: Default::default(),
        user: None,
        extra: Default::default(),
        tags: Default::default(),
        contexts: {
            let mut contexts = im::HashMap::new();
            if let Some(c) = utils::os_context() {
                contexts = contexts.insert("os".to_string(), c);
            }
            if let Some(c) = utils::rust_context() {
                contexts = contexts.insert("rust".to_string(), c);
            }
            if let Some(c) = utils::device_context() {
                contexts = contexts.insert("device".to_string(), c);
            }
            contexts
        },
    }
}

#[derive(Debug, Clone)]
struct StackLayer {
    client: Option<Arc<Client>>,
    scope: Scope,
}

impl Stack {
    pub fn for_process() -> Stack {
        Stack {
            layers: vec![
                StackLayer {
                    client: None,
                    scope: default_scope(),
                },
            ],
            ty: StackType::Process,
        }
    }

    pub fn for_thread() -> Stack {
        Stack {
            layers: vec![
                with_process_stack(|stack| StackLayer {
                    client: stack.client(),
                    scope: stack.scope().clone(),
                }),
            ],
            ty: StackType::Thread,
        }
    }

    pub fn push(&mut self) {
        let scope = self.layers[self.layers.len() - 1].clone();
        self.layers.push(scope);
    }

    pub fn pop(&mut self) {
        if self.layers.len() <= 1 {
            panic!("Pop from empty {:?} stack", self.ty)
        }
        self.layers.pop().unwrap();
    }

    pub fn bind_client(&mut self, client: Option<Arc<Client>>) {
        let depth = self.layers.len() - 1;
        self.layers[depth].client = client;
    }

    pub fn client(&self) -> Option<Arc<Client>> {
        self.layers[self.layers.len() - 1].client.clone()
    }

    pub fn scope(&self) -> &Scope {
        let idx = self.layers.len() - 1;
        &self.layers[idx].scope
    }

    pub fn scope_mut(&mut self) -> &mut Scope {
        let idx = self.layers.len() - 1;
        &mut self.layers[idx].scope
    }

    fn layer_token(&self) -> StackLayerToken {
        StackLayerToken(self as *const Stack, self.layers.len())
    }
}

fn is_main_thread() -> bool {
    let thread = thread::current();
    let raw_id: u64 = unsafe { mem::transmute(thread.id()) };
    raw_id == 0
}

fn with_process_stack<F, R>(f: F) -> R
where
    F: FnOnce(&Stack) -> R,
{
    f(&mut PROCESS_STACK.read().unwrap_or_else(|x| x.into_inner()))
}

fn with_process_stack_mut<F, R>(f: F) -> R
where
    F: FnOnce(&mut Stack) -> R,
{
    f(&mut PROCESS_STACK.write().unwrap_or_else(|x| x.into_inner()))
}

fn with_stack_mut<F, R>(f: F) -> R
where
    F: FnOnce(&mut Stack) -> R,
{
    if is_main_thread() {
        with_process_stack_mut(f)
    } else {
        THREAD_STACK.with(|stack| f(&mut *stack.borrow_mut()))
    }
}

fn with_stack<F, R>(f: F) -> R
where
    F: FnOnce(&Stack) -> R,
{
    if is_main_thread() {
        with_process_stack(f)
    } else {
        THREAD_STACK.with(|stack| f(&*stack.borrow()))
    }
}

/// Invokes a function if the sentry client is available with client and scope.
///
/// The function is invoked with the client and current scope (read-only) and permits
/// operations to be executed on the client.  This is useful when writing integration
/// code where potentially expensive operations should not be executed if Sentry is
/// not configured.
///
/// The return value must be `Default` so that it can be created even if Sentry is not
/// configured.
pub fn with_client_and_scope<F, R>(f: F) -> R
where
    F: FnOnce(Arc<Client>, &Scope) -> R,
    R: Default,
{
    with_stack(|stack| {
        if let Some(client) = stack.client() {
            f(client, stack.scope())
        } else {
            Default::default()
        }
    })
}

/// Crate internal helper for working with clients and mutable scopes.
pub(crate) fn with_client_and_scope_mut<F, R>(f: F) -> R
where
    F: FnOnce(Arc<Client>, &mut Scope) -> R,
    R: Default,
{
    with_stack_mut(|stack| {
        if let Some(client) = stack.client() {
            f(client, stack.scope_mut())
        } else {
            Default::default()
        }
    })
}

/// A scope guard.
#[derive(Default)]
pub struct ScopeGuard(Option<StackLayerToken>);

impl fmt::Debug for ScopeGuard {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "ScopeGuard")
    }
}

impl Drop for ScopeGuard {
    fn drop(&mut self) {
        if let Some(token) = self.0 {
            with_stack_mut(|stack| {
                if stack.layer_token() != token {
                    panic!("Current active stack does not match scope guard");
                } else {
                    stack.pop();
                }
            })
        }
    }
}

/// Pushes a new scope on the stack.
///
/// The currently bound client is propagated to the new scope and already existing
/// data on the scope is inherited.  Modifications done to the inner scope however
/// are isolated from the outer scope.
///
/// This returns a guard.  When the guard is collected the scope is popped again.
///
/// # Example
///
/// ```no_run
/// {
///     let _guard = sentry::push_scope();
///     sentry::configure_scope(|scope| {
///         scope.set_tag("some_tag", "some_value");
///     });
///     // until the end of the block the scope is changed.
/// }
/// ```
pub fn push_scope() -> ScopeGuard {
    with_stack_mut(|stack| {
        stack.push();
        ScopeGuard(Some(stack.layer_token()))
    })
}

/// Returns the currently bound client if there is one.
///
/// This might return `None` in case there is no client.  For the most part
/// code will not use this function but instead directly call `capture_event`
/// and similar functions which work on the currently active client.
pub fn current_client() -> Option<Arc<Client>> {
    with_client_impl! {{
        with_stack(|stack| stack.client())
    }}
}

/// Rebinds the client on the current scope.
///
/// The current scope is defined as the current thread.  If a new thread spawns
/// it inherits the client of the process.  The main thread is specially handled
/// in the sense that if the main thread binds a client it becomes bound to the
/// process.
pub fn bind_client(client: Arc<Client>) {
    with_client_impl! {{
        with_stack_mut(|stack| stack.bind_client(Some(client)));
    }}
}

/// Unbinds the client on the current scope.
///
/// This effectively prevents data collection and reporting on the current scope.
pub fn unbind_client() {
    with_client_impl! {{
        with_stack_mut(|stack| stack.bind_client(None));
    }}
}

impl Scope {
    /// Clear the scope.
    ///
    /// By default a scope will inherit all values from the higher scope.
    /// In some situations this might not be what a user wants.  Calling
    /// this method will wipe all data contained within.
    pub fn clear(&mut self) {
        *self = default_scope();
    }

    /// Sets the fingerprint.
    pub fn set_fingerprint(&mut self, fingerprint: Option<&[&str]>) {
        self.fingerprint =
            fingerprint.map(|fp| Arc::new(fp.iter().map(|x| Cow::Owned(x.to_string())).collect()))
    }

    /// Sets the transaction.
    pub fn set_transaction(&mut self, transaction: Option<&str>) {
        self.transaction = transaction.map(|txn| Arc::new(txn.to_string()));
    }

    /// Sets the user for the current scope.
    pub fn set_user(&mut self, user: Option<User>) {
        self.user = user.map(Arc::new);
    }

    /// Sets a tag to a specific value.
    pub fn set_tag<V: ToString>(&mut self, key: &str, value: V) {
        self.tags = self.tags.insert(key.to_string(), value.to_string());
    }

    /// Removes a tag.
    pub fn remove_tag(&mut self, key: &str) {
        // annoyingly this needs a String :(
        self.tags = self.tags.remove(&key.to_string());
    }

    /// Sets a context for a key.
    pub fn set_context<C: Into<Context>>(&mut self, key: &str, value: C) {
        self.contexts = self.contexts.insert(key.to_string(), value.into());
    }

    /// Removes a context for a key.
    pub fn remove_context(&mut self, key: &str) {
        // annoyingly this needs a String :(
        self.contexts = self.contexts.remove(&key.to_string());
    }

    /// Sets a extra to a specific value.
    pub fn set_extra(&mut self, key: &str, value: Value) {
        self.extra = self.extra.insert(key.to_string(), value);
    }

    /// Removes a extra.
    pub fn remove_extra(&mut self, key: &str) {
        // annoyingly this needs a String :(
        self.extra = self.extra.remove(&key.to_string());
    }
}