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
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
#![allow(missing_docs)]

use std::borrow::Cow;
use std::fmt;

use bitflags::bitflags;
use perf_event_open_sys::bindings;
use perf_event_open_sys::bindings::__BindgenBitfieldUnit;
use perf_event_open_sys::bindings::perf_branch_entry;
use perf_event_open_sys::bindings::perf_mem_data_src;

use crate::parse::ParseError;
use crate::prelude::*;
use crate::ReadGroup;
use crate::ReadValue;

mod sample_impl {
    use super::*;

    // We have this in its own module since it needs to be named `Sample` for the
    // `Debug` impl to look right. Plus, accessing any of the fields on this struct
    // will likely break things so better to have it in its own module so that can't
    // happen.
    option_struct! {
        pub(super) struct Sample<'a>: u32 {
            pub ip: u64,
            pub pid: u32,
            pub tid: u32,
            pub time: u64,
            pub addr: u64,
            pub id: u64,
            pub stream_id: u64,
            pub cpu: u32,
            pub period: u64,
            pub values: ReadGroup<'a>,
            pub callchain: Cow<'a, [u64]>,
            pub raw: Cow<'a, [u8]>,
            pub lbr_hw_index: u64,
            pub lbr: Cow<'a, [BranchEntry]>,
            pub regs_user: Registers<'a>,
            pub stack_user: Cow<'a, [u8]>,
            pub weight: u64,
            pub data_src: DataSource,
            pub transaction: Txn,
            pub regs_intr: Registers<'a>,
            pub phys_addr: u64,
            pub aux: Cow<'a, [u8]>,
            pub data_page_size: u64,
            pub code_page_size: u64
        }
    }
}

/// A sample emitted by the kernel.
///
/// See the [manpage] for documentation on what each of the individual fields
/// mean.
///
/// [manpage]: https://man7.org/linux/man-pages/man2/perf_event_open.2.html
#[derive(Clone)]
pub struct Sample<'a>(sample_impl::Sample<'a>);

#[allow(missing_docs)]
impl<'a> Sample<'a> {
    pub fn id(&self) -> Option<u64> {
        self.0.id().copied()
    }

    pub fn ip(&self) -> Option<u64> {
        self.0.ip().copied()
    }

    pub fn pid(&self) -> Option<u32> {
        self.0.pid().copied()
    }

    pub fn tid(&self) -> Option<u32> {
        self.0.tid().copied()
    }

    pub fn time(&self) -> Option<u64> {
        self.0.time().copied()
    }

    pub fn addr(&self) -> Option<u64> {
        self.0.addr().copied()
    }

    pub fn stream_id(&self) -> Option<u64> {
        self.0.stream_id().copied()
    }

    pub fn cpu(&self) -> Option<u32> {
        self.0.cpu().copied()
    }

    pub fn period(&self) -> Option<u64> {
        self.0.period().copied()
    }

    pub fn values(&self) -> Option<&ReadGroup<'a>> {
        self.0.values()
    }

    pub fn callchain(&self) -> Option<&[u64]> {
        self.0.callchain().map(|cow| &**cow)
    }

    pub fn raw(&self) -> Option<&[u8]> {
        self.0.raw().map(|cow| &**cow)
    }

    pub fn lbr_hw_index(&self) -> Option<u64> {
        self.0.lbr_hw_index().copied()
    }

    pub fn lbr(&self) -> Option<&[BranchEntry]> {
        self.0.lbr().map(|cow| &**cow)
    }

    pub fn regs_user(&self) -> Option<&Registers<'a>> {
        self.0.regs_user()
    }

    pub fn stack_user(&self) -> Option<&[u8]> {
        self.0.stack_user().map(|cow| &**cow)
    }

    pub fn weight(&self) -> Option<u64> {
        self.0.weight().copied()
    }

    pub fn data_src(&self) -> Option<DataSource> {
        self.0.data_src().copied()
    }

    pub fn transaction(&self) -> Option<Txn> {
        self.0.transaction().copied()
    }

    pub fn regs_intr(&self) -> Option<&Registers<'a>> {
        self.0.regs_intr()
    }

    pub fn phys_addr(&self) -> Option<u64> {
        self.0.phys_addr().copied()
    }

    pub fn aux(&self) -> Option<&[u8]> {
        self.0.aux().map(|cow| &**cow)
    }

    pub fn data_page_size(&self) -> Option<u64> {
        self.0.data_page_size().copied()
    }

    pub fn code_page_size(&self) -> Option<u64> {
        self.0.code_page_size().copied()
    }
}

impl<'p> Parse<'p> for Sample<'p> {
    fn parse<B, E>(p: &mut Parser<B, E>) -> ParseResult<Self>
    where
        E: Endian,
        B: ParseBuf<'p>,
    {
        let config = p.config();
        let sty = config.sample_type();
        let branch_hw_index = config.branch_hw_index();

        let id = p.parse_if(sty.contains(SampleFlags::IDENTIFIER))?;
        let ip = p.parse_if(sty.contains(SampleFlags::IP))?;
        let pid = p.parse_if(sty.contains(SampleFlags::TID))?;
        let tid = p.parse_if(sty.contains(SampleFlags::TID))?;
        let time = p.parse_if(sty.contains(SampleFlags::TIME))?;
        let addr = p.parse_if(sty.contains(SampleFlags::ADDR))?;
        let id = p.parse_if(sty.contains(SampleFlags::ID))?.or(id);
        let stream_id = p.parse_if(sty.contains(SampleFlags::STREAM_ID))?;
        let cpu = p.parse_if_with(sty.contains(SampleFlags::CPU), |p| {
            Ok((p.parse_u32()?, p.parse_u32()?).0)
        })?;
        let period = p.parse_if(sty.contains(SampleFlags::PERIOD))?;
        let values = p.parse_if_with(sty.contains(SampleFlags::READ), |p| {
            if p.config().read_format().contains(ReadFormat::GROUP) {
                p.parse()
            } else {
                ReadValue::parse(p).map(From::from)
            }
        })?;
        let callchain = p.parse_if_with(sty.contains(SampleFlags::CALLCHAIN), |p| {
            let nr = p.parse_u64()? as _;
            unsafe { p.parse_slice(nr) }
        })?;
        let raw = p.parse_if_with(sty.contains(SampleFlags::RAW), |p| {
            let size = p.parse_u64()? as _;
            p.parse_bytes(size)
        })?;
        let (lbr, lbr_hw_index) = p
            .parse_if_with(sty.contains(SampleFlags::BRANCH_STACK), |p| {
                let nr = p.parse_u64()? as usize;
                let hw_index = p.parse_if(branch_hw_index)?;
                let lbr = unsafe { p.parse_slice(nr)? };

                Ok((lbr, hw_index))
            })?
            .unzip();
        let lbr_hw_index = lbr_hw_index.flatten();
        let regs_user = p.parse_if_with(sty.contains(SampleFlags::REGS_USER), |p| {
            Registers::parse_user(p)
        })?;
        let stack_user = p.parse_if_with(sty.contains(SampleFlags::STACK_USER), |p| {
            let size = p.parse_u64()? as usize;
            let mut data = p.parse_bytes(size)?;
            let dyn_size = p.parse_u64()? as usize;

            if dyn_size > data.len() {
                return Err(ParseError::custom(
                    ErrorKind::InvalidRecord,
                    "stack dyn_size was greater than the record size",
                ));
            }

            match &mut data {
                Cow::Owned(data) => data.truncate(dyn_size),
                Cow::Borrowed(data) => *data = &data[..dyn_size],
            }

            Ok(data)
        })?;
        let weight = p.parse_if(sty.contains(SampleFlags::WEIGHT))?;
        let data_src = p.parse_if(sty.contains(SampleFlags::DATA_SRC))?;
        let transaction = p.parse_if(sty.contains(SampleFlags::TRANSACTION))?;
        let regs_intr = p.parse_if_with(sty.contains(SampleFlags::REGS_INTR), |p| {
            Registers::parse_intr(p)
        })?;
        let phys_addr = p.parse_if(sty.contains(SampleFlags::PHYS_ADDR))?;
        let aux = p.parse_if_with(sty.contains(SampleFlags::AUX), |p| {
            let size = p.parse_u64()? as usize;
            p.parse_bytes(size)
        })?;
        let data_page_size = p.parse_if(sty.contains(SampleFlags::DATA_PAGE_SIZE))?;
        let code_page_size = p.parse_if(sty.contains(SampleFlags::CODE_PAGE_SIZE))?;

        Ok(Self(sample_impl::Sample::new(
            ip,
            pid,
            tid,
            time,
            addr,
            id,
            stream_id,
            cpu,
            period,
            values,
            callchain,
            raw,
            lbr_hw_index,
            lbr,
            regs_user,
            stack_user,
            weight,
            data_src,
            transaction,
            regs_intr,
            phys_addr,
            aux,
            data_page_size,
            code_page_size,
        )))
    }
}

impl fmt::Debug for Sample<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.0.fmt(f)
    }
}

/// Describes the captured subset of registers when a sample was taken.
///
/// See the [manpage] for all the details.
///
/// [manpage]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html
#[derive(Clone, Debug)]
pub struct Registers<'a> {
    /// The ABI of the program from which the sample was taken.
    pub abi: SampleRegsAbi,

    /// A bitmask indicating which registers were recorded.
    ///
    /// This is configured as a part of constructing the sampler.
    pub mask: u64,

    /// The recorded values of the registers.
    pub regs: Cow<'a, [u64]>,
}

c_enum! {
    /// ABI of the program when sampling registers.
    pub struct SampleRegsAbi : u64 {
        const NONE = bindings::PERF_SAMPLE_REGS_ABI_NONE as _;
        const ABI_32 = bindings::PERF_SAMPLE_REGS_ABI_32 as _;
        const ABI_64 = bindings::PERF_SAMPLE_REGS_ABI_64 as _;
    }
}

impl<'p> Registers<'p> {
    /// Parse registers using the user registers mask in the config.
    pub fn parse_user<B, E>(p: &mut Parser<B, E>) -> ParseResult<Self>
    where
        E: Endian,
        B: ParseBuf<'p>,
    {
        Self::parse(p, p.config().regs_user())
    }

    /// Parse registers using the intr registers mask in the config.
    pub fn parse_intr<B, E>(p: &mut Parser<B, E>) -> ParseResult<Self>
    where
        E: Endian,
        B: ParseBuf<'p>,
    {
        Self::parse(p, p.config().regs_intr())
    }

    fn parse<B, E>(p: &mut Parser<B, E>, mask: u64) -> ParseResult<Self>
    where
        E: Endian,
        B: ParseBuf<'p>,
    {
        Ok(Self {
            abi: p.parse()?,
            mask,
            regs: unsafe { p.parse_slice(mask.count_ones() as _)? },
        })
    }
}

impl<'p> Parse<'p> for SampleRegsAbi {
    fn parse<B, E>(p: &mut Parser<B, E>) -> ParseResult<Self>
    where
        E: Endian,
        B: ParseBuf<'p>,
    {
        Ok(Self::new(p.parse()?))
    }
}

c_enum! {
    /// Branch type as used by the last branch record.
    ///
    /// This is a field present within [`BranchEntry`]. It is not documented in the
    /// [manpage] but is present within the perf_event headers.
    ///
    /// [manpage]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html
    pub struct BranchType : u8 {
        const UNKNOWN = bindings::PERF_BR_UNKNOWN as _;
        const COND = bindings::PERF_BR_COND as _;
        const UNCOND = bindings::PERF_BR_UNCOND as _;
        const IND = bindings::PERF_BR_IND as _;
        const CALL = bindings::PERF_BR_CALL as _;
        const IND_CALL = bindings::PERF_BR_IND_CALL as _;
        const RET = bindings::PERF_BR_RET as _;
        const SYSCALL = bindings::PERF_BR_SYSCALL as _;
        const COND_CALL = bindings::PERF_BR_COND_CALL as _;
        const COND_RET = bindings::PERF_BR_COND_RET as _;
    }

}

/// Record of a branch taken by the hardware.
#[derive(Copy, Clone, Debug)]
pub struct BranchEntry(perf_branch_entry);

impl BranchEntry {
    /// Address of the source instruction.
    ///
    /// This may not always be a branch instruction.
    pub fn from(&self) -> u64 {
        self.0.from
    }

    /// Address of the branch target.
    pub fn to(&self) -> u64 {
        self.0.to
    }

    /// Whether the branch was mispredicted.
    pub fn mispred(&self) -> bool {
        self.0.mispred() != 0
    }

    /// Whether the branch was predicted correctly.
    pub fn predicted(&self) -> bool {
        self.0.predicted() != 0
    }

    /// Whether the branch occurred within a transaction.
    pub fn in_tx(&self) -> bool {
        self.0.in_tx() != 0
    }

    /// Whether the branch was due to a transaction abort.
    pub fn abort(&self) -> bool {
        self.0.abort() != 0
    }

    /// The cycle count since the last branch.
    pub fn cycles(&self) -> u16 {
        self.0.cycles() as _
    }

    /// Branch type.
    ///
    /// This field is not documented within the manpage but is present within
    /// the perf_event headers.
    pub fn ty(&self) -> BranchType {
        BranchType(self.0.type_() as _)
    }
}

impl<'p> Parse<'p> for BranchEntry {
    fn parse<B, E>(p: &mut Parser<B, E>) -> ParseResult<Self>
    where
        E: Endian,
        B: ParseBuf<'p>,
    {
        Ok(Self(perf_branch_entry {
            from: p.parse()?,
            to: p.parse()?,
            _bitfield_align_1: [],
            _bitfield_1: __BindgenBitfieldUnit::new(u64::to_ne_bytes(p.parse()?)),
        }))
    }
}

/// Describes where in the memory hierarchy the sampled instruction came from.
///
/// See the [manpage] for a full description.
///
/// [manpage]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html
#[derive(Copy, Clone, Default)]
pub struct DataSource(perf_mem_data_src);

impl DataSource {
    fn bitfield(&self) -> &bindings::perf_mem_data_src__bindgen_ty_1 {
        unsafe { &self.0.__bindgen_anon_1 }
    }

    /// Type of opcode.
    pub fn mem_op(&self) -> MemOp {
        MemOp::from_bits_retain(self.bitfield().mem_op())
    }

    /// Memory hierarchy level hit or miss.
    pub fn mem_lvl(&self) -> MemLevel {
        MemLevel::from_bits_retain(self.bitfield().mem_lvl())
    }

    /// Snoop mode.
    ///
    /// This is a combination of the flags from both the `mem_snoop` and the
    /// `mem_snoopx` fields in the kernel source.
    pub fn mem_snoop(&self) -> MemSnoop {
        MemSnoop::new(self.bitfield().mem_snoop(), self.bitfield().mem_snoopx())
    }

    /// Lock instruction.
    pub fn mem_lock(&self) -> MemLock {
        MemLock::from_bits_retain(self.bitfield().mem_lock())
    }

    /// TLB access hit or miss.
    pub fn mem_dtlb(&self) -> MemDtlb {
        MemDtlb::from_bits_retain(self.bitfield().mem_dtlb())
    }

    /// Memory hierarchy level number.
    ///
    /// This field is not documented in the [manpage] but is present within the
    /// kernel headers.
    ///
    /// [manpage]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html
    pub fn mem_lvl_num(&self) -> MemLevelNum {
        MemLevelNum(self.bitfield().mem_lvl_num() as _)
    }

    /// Whether the memory access was remote.
    ///
    /// This field is not documented in the [manpage] but is present within the
    /// kernel headers.
    ///
    /// [manpage]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html
    pub fn mem_remote(&self) -> bool {
        self.bitfield().mem_remote() != 0
    }

    /// Access was blocked.
    ///
    /// This field is not documented in the [manpage] but is present within the
    /// kernel headers.
    ///
    /// [manpage]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html
    pub fn mem_blk(&self) -> MemBlk {
        MemBlk::from_bits_retain(self.bitfield().mem_blk())
    }

    /// The number of hops that were required to perform this memory access.
    ///
    /// This field is not document in the [man page] but is present within the
    /// kernel headers.
    ///
    /// [man page]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html
    pub fn mem_hops(&self) -> u8 {
        self.bitfield().mem_hops() as _
    }
}

impl fmt::Debug for DataSource {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("DataSource")
            .field("mem_op", &self.mem_op())
            .field("mem_lvl", &self.mem_lvl())
            .field("mem_snoop", &self.mem_snoop())
            .field("mem_lock", &self.mem_lock())
            .field("mem_dtlb", &self.mem_dtlb())
            .field("mem_lvl_num", &self.mem_lvl_num())
            .field("mem_remote", &self.mem_remote())
            .field("mem_blk", &self.mem_blk())
            .field("mem_hops", &self.mem_hops())
            .finish()
    }
}

impl<'p> Parse<'p> for DataSource {
    fn parse<B, E>(p: &mut Parser<B, E>) -> ParseResult<Self>
    where
        E: Endian,
        B: ParseBuf<'p>,
    {
        Ok(Self(perf_mem_data_src { val: p.parse()? }))
    }
}

bitflags! {
    /// Memory operation.
    ///
    /// This is used by [`DataSource`].
    #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
    pub struct MemOp : u64 {
        const NA = bindings::PERF_MEM_OP_NA as _;
        const LOAD = bindings::PERF_MEM_OP_LOAD as _;
        const STORE = bindings::PERF_MEM_OP_STORE as _;
        const PFETCH = bindings::PERF_MEM_OP_PFETCH as _;
        const EXEC = bindings::PERF_MEM_OP_EXEC as _;
    }

    /// Location in the memory hierarchy.
    ///
    /// This is used by [`DataSource`].
    #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
    pub struct MemLevel : u64 {
        const NA = bindings::PERF_MEM_LVL_NA as _;
        const HIT = bindings::PERF_MEM_LVL_HIT as _;
        const MISS = bindings::PERF_MEM_LVL_MISS as _;
        const L1 = bindings::PERF_MEM_LVL_L1 as _;
        const LFB = bindings::PERF_MEM_LVL_LFB as _;
        const L2 = bindings::PERF_MEM_LVL_L2 as _;
        const L3 = bindings::PERF_MEM_LVL_L3 as _;
        const LOC_RAM = bindings::PERF_MEM_LVL_LOC_RAM as _;
        const REM_RAM1 = bindings::PERF_MEM_LVL_REM_RAM1 as _;
        const REM_RAM2 = bindings::PERF_MEM_LVL_REM_RAM2 as _;
        const REM_CCE1 = bindings::PERF_MEM_LVL_REM_CCE1 as _;
        const REM_CCE2 = bindings::PERF_MEM_LVL_REM_CCE2 as _;
        const IO = bindings::PERF_MEM_LVL_IO as _;
        const UNC = bindings::PERF_MEM_LVL_UNC as _;
    }

    /// Whether the instruction was a locked instruction.
    ///
    /// This is used by [`DataSource`].
    #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
    pub struct MemLock : u64 {
        const NA = bindings::PERF_MEM_LOCK_NA as _;
        const LOCKED = bindings::PERF_MEM_LOCK_LOCKED as _;
    }

    /// Memory TLB access.
    ///
    /// This is used by [`DataSource`].
    #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
    pub struct MemDtlb : u64 {
        const NA = bindings::PERF_MEM_TLB_NA as _;
        const HIT = bindings::PERF_MEM_TLB_HIT as _;
        const MISS = bindings::PERF_MEM_TLB_MISS as _;
        const L1 = bindings::PERF_MEM_TLB_L1 as _;
        const L2 = bindings::PERF_MEM_TLB_L2 as _;
        const WK = bindings::PERF_MEM_TLB_WK as _;
        const OS = bindings::PERF_MEM_TLB_OS as _;
    }

    /// Extended bits for [`MemSnoop`].
    ///
    /// This is used by [`DataSource`].
    #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
    pub struct MemSnoopX : u64 {
        const FWD = bindings::PERF_MEM_SNOOPX_FWD as _;
        const PEER = bindings::PERF_MEM_SNOOPX_PEER as _;
    }

    /// Access was blocked.
    #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
    pub struct MemBlk : u64 {
        const NA = bindings::PERF_MEM_BLK_NA as _;
        const DATA = bindings::PERF_MEM_BLK_DATA as _;
        const ADDR = bindings::PERF_MEM_BLK_ADDR as _;
    }
}

bitflags! {
    /// Memory snoop mode.
    ///
    /// This is used by [`DataSource`].
    #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
    pub struct MemSnoop : u64 {
        const NA   = (bindings::PERF_MEM_SNOOP_NA   as u64) << bindings::PERF_MEM_SNOOP_SHIFT;
        const NONE = (bindings::PERF_MEM_SNOOP_NONE as u64) << bindings::PERF_MEM_SNOOP_SHIFT;
        const HIT  = (bindings::PERF_MEM_SNOOP_HIT  as u64) << bindings::PERF_MEM_SNOOP_SHIFT;
        const MISS = (bindings::PERF_MEM_SNOOP_MISS as u64) << bindings::PERF_MEM_SNOOP_SHIFT;
        const HITM = (bindings::PERF_MEM_SNOOP_HITM as u64) << bindings::PERF_MEM_SNOOP_SHIFT;

        const FWD  = (bindings::PERF_MEM_SNOOPX_FWD  as u64) << bindings::PERF_MEM_SNOOPX_SHIFT;
        const PEER = (bindings::PERF_MEM_SNOOPX_PEER as u64) << bindings::PERF_MEM_SNOOPX_SHIFT;
    }
}

impl MemSnoop {
    pub fn new(mut mem_snoop: u64, mut mem_snoopx: u64) -> Self {
        mem_snoop &= Self::SNOOP_MASK;
        mem_snoopx &= Self::SNOOPX_MASK;

        Self::from_bits_truncate(
            (mem_snoop << bindings::PERF_MEM_SNOOP_SHIFT)
                | (mem_snoopx << bindings::PERF_MEM_SNOOPX_SHIFT),
        )
    }

    const SNOOP_MASK: u64 = 0b11111;
    const SNOOPX_MASK: u64 = 0b11;
}

bitflags! {
    /// Info about a transactional memory event.
    #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
    pub struct Txn: u64 {
        const ELISION = bindings::PERF_TXN_ELISION as _;
        const TRANSACTION = bindings::PERF_TXN_TRANSACTION as _;
        const SYNC = bindings::PERF_TXN_SYNC as _;
        const ASYNC = bindings::PERF_TXN_ASYNC as _;
        const RETRY = bindings::PERF_TXN_RETRY as _;
        const CONFLICT = bindings::PERF_TXN_CONFLICT as _;
        const CAPACITY_WRITE = bindings::PERF_TXN_CAPACITY_WRITE as _;
        const CAPACITY_READ = bindings::PERF_TXN_CAPACITY_READ as _;

        const ABORT_MASK = bindings::PERF_TXN_ABORT_MASK as _;
    }
}

impl Txn {
    /// A user-specified abort code.
    pub fn abort(&self) -> u32 {
        (self.bits() >> bindings::PERF_TXN_ABORT_SHIFT) as _
    }
}

impl<'p> Parse<'p> for Txn {
    fn parse<B, E>(p: &mut Parser<B, E>) -> ParseResult<Self>
    where
        E: Endian,
        B: ParseBuf<'p>,
    {
        Ok(Self::from_bits_retain(p.parse()?))
    }
}

c_enum! {
    /// Memory hierarchy level number.
    ///
    /// This is a field within [`DataSource`]. It is not documented in the [manpage]
    /// but is present within the perf_event headers.
    ///
    /// [manpage]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html
    pub struct MemLevelNum : u8 {
        const L1 = bindings::PERF_MEM_LVLNUM_L1 as _;
        const L2 = bindings::PERF_MEM_LVLNUM_L2 as _;
        const L3 = bindings::PERF_MEM_LVLNUM_L3 as _;
        const L4 = bindings::PERF_MEM_LVLNUM_L4 as _;

        const ANY_CACHE = bindings::PERF_MEM_LVLNUM_ANY_CACHE as _;
        const LFB = bindings::PERF_MEM_LVLNUM_LFB as _;
        const RAM = bindings::PERF_MEM_LVLNUM_RAM as _;
        const PMEM = bindings::PERF_MEM_LVLNUM_PMEM as _;
        const NA = bindings::PERF_MEM_LVLNUM_NA as _;
    }
}

#[cfg(test)]
mod tests {
    use crate::endian::Little;

    use super::*;

    #[test]
    fn simple_parse_sample() {
        #[rustfmt::skip]
        let data: &[u8] = &[
            0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
            0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
        ];

        let config: ParseConfig<Little> =
            ParseConfig::default().with_sample_type(SampleFlags::ADDR | SampleFlags::ID);
        let sample: Sample = Parser::new(data, config).parse().unwrap();

        assert_eq!(sample.addr(), Some(0x0706050403020100));
        assert_eq!(sample.id(), Some(0x0F0E0D0C0B0A0908));
        assert_eq!(sample.cpu(), None);
        assert_eq!(sample.time(), None);
    }
}