use error::*;
use intern::{Interner, Symbol};
#[cfg(feature = "serde")]
use intern::SerializeWithInterner;
use reader::Reader;
use byteorder::{BigEndian, ByteOrder};
#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::{fmt, u64};
use std::collections::BTreeMap;
use std::fs::File;
use std::io::BufReader;
use std::path::{Path, PathBuf};
#[cfg(feature = "serde")]
use std::result::Result as StdResult;
use std::str::FromStr;
derive_serialize_with_interner! {
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Gcov {
pub ty: Type,
pub version: Version,
pub stamp: u32,
pub records: Vec<Record>,
#[serde(skip)]
pub src: Option<PathBuf>,
}
}
impl Gcov {
pub fn open<P: AsRef<Path>>(p: P, interner: &mut Interner) -> Result<Gcov> {
debug!("open gcov file {:?}", p.as_ref());
let src = p.as_ref().to_owned();
Location::File(src.clone()).wrap(|| -> Result<Gcov> {
let reader = BufReader::new(File::open(p)?);
let mut gcov = Reader::new(reader, interner)?.parse()?;
gcov.src = Some(src);
Ok(gcov)
})
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Type {
Gcno,
Gcda,
}
impl fmt::Display for Type {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.write_str(match *self {
Type::Gcno => "gcno",
Type::Gcda => "gcda",
})
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Tag(pub u32);
pub const EOF_TAG: Tag = Tag(0);
pub const FUNCTION_TAG: Tag = Tag(0x01_00_00_00);
pub const BLOCKS_TAG: Tag = Tag(0x01_41_00_00);
pub const ARCS_TAG: Tag = Tag(0x01_43_00_00);
pub const LINES_TAG: Tag = Tag(0x01_45_00_00);
pub const COUNTER_BASE_TAG: Tag = Tag(0x01_a1_00_00);
pub const OBJECT_SUMMARY_TAG: Tag = Tag(0xa1_00_00_00);
pub const PROGRAM_SUMMARY_TAG: Tag = Tag(0xa3_00_00_00);
pub const AFDO_FILE_NAMES_TAG: Tag = Tag(0xaa_00_00_00);
pub const AFDO_FUNCTION_TAG: Tag = Tag(0xac_00_00_00);
pub const AFDO_WORKING_SET_TAG: Tag = Tag(0xaf_00_00_00);
impl fmt::Display for Tag {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "0x{:08x}", self.0)
}
}
impl fmt::Debug for Tag {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "Tag(0x{:08x})", self.0)
}
}
impl fmt::LowerHex for Tag {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(fmt)
}
}
impl fmt::UpperHex for Tag {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(fmt)
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct Version(u32);
pub const INVALID_VERSION: Version = Version(0);
pub const VERSION_4_7: Version = Version(0x34_30_37_2a);
impl Version {
pub fn try_from(raw_version: u32) -> Result<Version> {
ensure!(raw_version & 0x80_80_80_ff == 0x2a, ErrorKind::UnsupportedVersion(raw_version));
Ok(Version(raw_version))
}
}
impl fmt::Debug for Version {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "Tag(\"{}\")", self)
}
}
impl fmt::Display for Version {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
use std::fmt::Write;
fmt.write_char((self.0 >> 24 & 0xff) as u8 as char)?;
fmt.write_char((self.0 >> 16 & 0xff) as u8 as char)?;
fmt.write_char((self.0 >> 8 & 0xff) as u8 as char)?;
fmt.write_char((self.0 & 0xff) as u8 as char)?;
Ok(())
}
}
impl FromStr for Version {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
ensure!(s.len() == 4, ErrorKind::UnsupportedVersion(0));
let raw_version = BigEndian::read_u32(s.as_bytes());
Version::try_from(raw_version)
}
}
#[cfg(feature = "serde")]
impl Serialize for Version {
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
serializer.collect_str(self)
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for Version {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
use serde::de::Error;
let s = <&'de str>::deserialize(deserializer)?;
Version::from_str(s).map_err(D::Error::custom)
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Record {
Function(Ident, Function),
Blocks(Blocks),
Arcs(Arcs),
Lines(Lines),
ArcCounts(ArcCounts),
Summary(Summary),
}
#[cfg(feature = "serde")]
impl SerializeWithInterner for Record {
fn serialize_with_interner<S: Serializer>(&self, serializer: S, interner: &Interner) -> StdResult<S::Ok, S::Error> {
match *self {
Record::Function(ref ident, ref function) => {
use serde::ser::SerializeTupleVariant;
let mut state = serializer.serialize_tuple_variant("Record", 0, "Function", 2)?;
state.serialize_field(ident)?;
state.serialize_field(&function.with_interner(interner))?;
state.end()
},
Record::Lines(ref lines) => serializer.serialize_newtype_variant("Record", 3, "Lines", &lines.with_interner(interner)),
_ => self.serialize(serializer),
}
}
}
derive_serialize_with_interner! {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Function {
pub lineno_checksum: u32,
pub cfg_checksum: u32,
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
pub source: Option<Source>,
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Ident(pub u32);
impl fmt::Debug for Ident {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "Ident({})", self.0)
}
}
impl fmt::Display for Ident {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(fmt)
}
}
derive_serialize_with_interner! {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
#[cfg_attr(feature="serde", derive(Serialize, Deserialize))]
pub struct Source {
pub name: Symbol,
pub filename: Symbol,
pub line: u32,
}
}
macro_rules! derive_serde_for_attr {
($flags:path, $kind:expr, $allowed_from_gcno:expr) => {
#[cfg(feature="serde")]
impl Serialize for $flags {
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
serializer.serialize_u16(self.bits())
}
}
#[cfg(feature="serde")]
impl<'de> Deserialize<'de> for $flags {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
use ::serde::de::Error;
let b = u16::deserialize(deserializer)?;
<$flags>::from_bits(b).ok_or_else(|| D::Error::custom(ErrorKind::UnsupportedAttr($kind, b as u32)))
}
}
impl $flags {
pub fn from_gcno(flags: u32) -> Result<$flags> {
ensure!(flags & !($allowed_from_gcno.bits() as u32) == 0, ErrorKind::UnsupportedAttr($kind, flags));
Ok(<$flags>::from_bits_truncate(flags as u16))
}
}
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Blocks {
pub flags: Vec<BlockAttr>,
}
bitflags! {
#[derive(Default)]
pub struct BlockAttr: u16 {
const UNEXPECTED = 2;
const CALL_SITE = 0x1000;
const CALL_RETURN = 0x2000;
const NONLOCAL_RETURN = 0x4000;
const EXCEPTIONAL = 0x8000;
}
}
derive_serde_for_attr! {
BlockAttr, "block", BlockAttr::UNEXPECTED
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct BlockIndex(pub u32);
impl fmt::Debug for BlockIndex {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "BI({})", self.0)
}
}
impl From<BlockIndex> for usize {
fn from(i: BlockIndex) -> usize {
i.0 as usize
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Arcs {
pub src_block: BlockIndex,
pub arcs: Vec<Arc>,
}
bitflags! {
#[derive(Default)]
pub struct ArcAttr: u16 {
const ON_TREE = 1;
const FAKE = 2;
const FALLTHROUGH = 4;
const THROW = 0x10;
const CALL_NON_RETURN = 0x20;
const NONLOCAL_RETURN = 0x40;
const UNCONDITIONAL = 0x80;
}
}
derive_serde_for_attr! {
ArcAttr, "arc", ArcAttr::ON_TREE | ArcAttr::FAKE | ArcAttr::FALLTHROUGH
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Arc {
pub dest_block: BlockIndex,
pub flags: ArcAttr,
}
derive_serialize_with_interner! {
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature="serde", derive(Serialize, Deserialize))]
pub struct Lines {
pub block_number: BlockIndex,
pub lines: Vec<Line>,
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(untagged))]
pub enum Line {
LineNumber(u32),
FileName(Symbol),
}
impl fmt::Debug for Line {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
Line::LineNumber(ref n) => write!(fmt, "Line({:?})", n),
Line::FileName(ref n) => write!(fmt, "Line({:?})", n),
}
}
}
#[cfg(feature = "serde")]
impl SerializeWithInterner for Line {
fn serialize_with_interner<S: Serializer>(&self, serializer: S, interner: &Interner) -> StdResult<S::Ok, S::Error> {
match *self {
Line::LineNumber(number) => number.serialize(serializer),
Line::FileName(symbol) => symbol.serialize_with_interner(serializer, interner),
}
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ArcCounts {
pub counts: Vec<u64>,
}
#[derive(Clone, PartialEq, Eq, Hash, Debug, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Summary {
pub checksum: u32,
pub num: u32,
pub runs: u32,
pub sum: u64,
pub max: u64,
pub sum_max: u64,
pub histogram: Option<Histogram>,
}
#[derive(Clone, PartialEq, Eq, Hash, Debug, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Histogram {
pub buckets: BTreeMap<u32, HistogramBucket>,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct HistogramBucket {
pub num: u32,
pub min: u64,
pub sum: u64,
}
impl Default for HistogramBucket {
fn default() -> HistogramBucket {
HistogramBucket {
num: 0,
min: u64::MAX,
sum: 0,
}
}
}
derive_serialize_with_interner! {
direct: Type, Tag, Version, Ident, BlockAttr, ArcAttr, Blocks, BlockIndex, Arcs, ArcCounts, Summary
}