From 9827764fca5baf47ab92e3a00b440bbcef3e0f69 Mon Sep 17 00:00:00 2001 From: Omkar Chandorkar Date: Fri, 11 Sep 2026 12:45:16 +0530 Subject: [PATCH 01/42] extractor: Add staged source-aware extraction Signed-off-by: Omkar Chandorkar --- README.md | 9 +- src/lib.rs | 580 +++++++++++++++++++++++++++++++------- src/main.rs | 13 +- src/python.rs | 6 + tests/delta_extraction.rs | 520 ++++++++++++++++++++++++++++++++++ tests/python_smoke.py | 4 +- 6 files changed, 1029 insertions(+), 103 deletions(-) create mode 100644 tests/delta_extraction.rs diff --git a/README.md b/README.md index 5da5dd6..3bdc4d2 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,9 @@ otadump ota.zip # Run on payload.bin file. otadump payload.bin + +# Apply a SOURCE_COPY delta payload with matching base partition images. +otadump delta.zip --output-dir output --source-dir source-images ``` ### Python @@ -97,11 +100,13 @@ otadump.extract( Path("output"), partitions=["boot", "system"], overwrite=True, + source_dir=Path("source-images"), ) ``` -The optional keyword arguments are `num_threads`, `overwrite`, `partitions`, -and `verify`. Extraction releases the Python GIL. +The optional keyword arguments are `num_threads`, `overwrite`, `partitions`, `verify`, and `source_dir`. +Set `source_dir` to a directory containing matching base partition images for supported delta operations. +Extraction releases the Python GIL. ## Contributors diff --git a/src/lib.rs b/src/lib.rs index 5da31f5..87ea92a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,11 +9,11 @@ mod python; use std::cmp::Reverse; use std::collections::HashSet; -use std::fs::{self, File, OpenOptions}; +use std::fs::{self, File}; use std::io::Read; use std::num::NonZero; -use std::ops::Mul; -use std::path::Path; +use std::ops::Range; +use std::path::{Component, Path, PathBuf}; use std::sync::OnceLock; use std::sync::atomic::{AtomicUsize, Ordering}; use std::{error, result, slice, thread}; @@ -28,6 +28,7 @@ use prost::Message as _; use rayon::{BroadcastContext, ThreadPoolBuilder}; use ring::digest; use sync_unsafe_cell::SyncUnsafeCell; +use tempfile::TempPath; use zip::ZipArchive; use zip::result::ZipError; use zstd::Decoder as ZstdDecoder; @@ -44,6 +45,7 @@ pub struct ExtractOptions<'a> { partitions: Option>, progress_reporter: &'a dyn ProgressReporter, verify: bool, + source_dir: Option, } impl<'a> ExtractOptions<'a> { @@ -55,6 +57,7 @@ impl<'a> ExtractOptions<'a> { partitions: None, progress_reporter: &NoOpProgressReporter, verify: true, + source_dir: None, } } @@ -97,24 +100,21 @@ impl<'a> ExtractOptions<'a> { let extract_ops = manifest.partitions.iter().map(|update| update.operations.len()).sum::(); - let verify_ops = if self.verify { - manifest - .partitions - .iter() - .map(|update| { - let partition_size = - update.new_partition_info.as_ref().and_then(|info| info.size).unwrap_or(0) - as usize; - partition_size.div_ceil(VERIFY_CHUNK_SIZE) - }) - .sum() - } else { - 0 - }; + let verify_ops: usize = manifest + .partitions + .iter() + .map(|update| { + let partition_size = + update.new_partition_info.as_ref().and_then(|info| info.size).unwrap_or(0) + as usize; + partition_size.div_ceil(VERIFY_CHUNK_SIZE) + }) + .sum(); let total_ops = extract_ops + verify_ops; let total_ops_completed = AtomicUsize::new(0); let block_size = manifest.block_size.context("block_size not defined")? as usize; + ensure!(block_size > 0, "block_size must be greater than zero"); // Ensure that all partitions to be extracted are present in the manifest. for partition_name in self.partitions.iter().flatten() { @@ -127,6 +127,51 @@ impl<'a> ExtractOptions<'a> { fs::create_dir_all(output_dir) .with_context(|| format!("Could not create output directory: {output_dir:?}"))?; + let sources = manifest + .partitions + .iter() + .map(|update| self.validate_partition(update, &payload, block_size)) + .collect::>>()?; + + let mut partition_names = HashSet::with_capacity(manifest.partitions.len()); + let mut destinations = Vec::with_capacity(manifest.partitions.len()); + for update in &manifest.partitions { + ensure!( + partition_names.insert(&update.partition_name), + "Duplicate partition mapping: {:?}", + update.partition_name + ); + destinations.push(output_dir.join(format!("{}.img", update.partition_name))); + } + for (update, destination) in manifest.partitions.iter().zip(&destinations) { + for source in sources.iter().flatten() { + ensure!( + !paths_alias(&source.path, destination)?, + "Source partition image {:?} and destination partition image {destination:?} refer to the same file", + source.path, + ); + } + if !self.overwrite { + ensure!( + !path_entry_exists(destination)?, + "Destination partition image already exists for {:?}: {destination:?}", + update.partition_name + ); + } + } + + let staging_dir = tempfile::Builder::new() + .prefix(".otadump-") + .tempdir_in(output_dir) + .context("Unable to create staging directory")?; + let mut staging_paths = Vec::with_capacity(manifest.partitions.len()); + let mut partitions = Vec::with_capacity(manifest.partitions.len()); + for update in &manifest.partitions { + let (path, partition) = self.create_staged_partition(update, staging_dir.path())?; + staging_paths.push(path); + partitions.push(partition); + } + let num_threads = self .num_threads .unwrap_or_else(|| thread::available_parallelism().map(NonZero::get).unwrap_or(1)) @@ -137,20 +182,22 @@ impl<'a> ExtractOptions<'a> { .context("Unable to start threadpool")?; let mut error = OnceLock::new(); - threadpool.in_place_scope_fifo(|scope| -> Result<()> { - for update in &manifest.partitions { + threadpool.in_place_scope_fifo(|scope| { + for ((update, source), partition) in + manifest.partitions.iter().zip(sources).zip(partitions) + { // Exit early if an error has occurred. if error.get().is_some() { break; } // Create and broadcast a task to the threadpool. - let partition = self.open_partition_file(update, output_dir)?; let task = Task { payload: &payload, block_size, verify: self.verify, update, + source, op_idx: AtomicUsize::new(0), partition: SyncUnsafeCell::new(partition), total_ops, @@ -164,13 +211,21 @@ impl<'a> ExtractOptions<'a> { } }); } - Ok(()) - })?; + }); if let Some(e) = error.take() { return Err(e); } + for (staging_path, destination) in staging_paths.into_iter().zip(destinations) { + let result = if self.overwrite { + staging_path.persist(&destination) + } else { + staging_path.persist_noclobber(&destination) + }; + result.map_err(|error| error.error)?; + } + self.progress_reporter.report_progress(1.); Ok(()) } @@ -219,33 +274,162 @@ impl<'a> ExtractOptions<'a> { } } - fn open_partition_file( + fn validate_partition( &self, update: &PartitionUpdate, - partition_dir: impl AsRef, - ) -> Result { + payload: &Payload<'_>, + block_size: usize, + ) -> Result> { + validate_partition_name(&update.partition_name)?; + let partition_len = update .new_partition_info .as_ref() .and_then(|info| info.size) .context("Unable to determine output file size")?; + let partition_len = + usize::try_from(partition_len).context("Output partition is too large")?; + ensure!( + update.new_partition_info.as_ref().and_then(|info| info.hash.as_ref()).is_some(), + "Unable to determine output partition hash" + ); + + let needs_source = update.operations.iter().try_fold(false, |needed, op| { + let op_type = Type::try_from(op.r#type).context("Invalid operation")?; + validate_operation_type(op_type)?; + Ok::<_, Error>(needed || operation_needs_source(op_type)) + })?; + let source = if needs_source { Some(self.open_source_partition(update)?) } else { None }; + + let mut destination_ranges = Vec::new(); + for (op_index, op) in update.operations.iter().enumerate() { + let op_type = Type::try_from(op.r#type).context("Invalid operation")?; + let dst_ranges = extent_ranges(&op.dst_extents, block_size, partition_len) + .with_context(|| format!("Invalid destination extents for operation {op_index}"))?; + ensure!(!dst_ranges.is_empty(), "Operation {op_index} has no destination extents"); + let dst_len = ranges_len(&dst_ranges)?; + if let Some(declared_len) = op.dst_length { + ensure!( + usize::try_from(declared_len).ok() == Some(dst_len), + "Destination length mismatch for operation {op_index}" + ); + } + destination_ranges.extend( + dst_ranges + .iter() + .filter(|range| !range.is_empty()) + .map(|range| (range.clone(), op_index)), + ); - let filename = Path::new(&update.partition_name).with_extension("img"); - let path = partition_dir.as_ref().join(filename); - - let file = OpenOptions::new() - .create(true) - .create_new(!self.overwrite) - .read(true) - .write(true) - .truncate(true) - .open(&path) - .with_context(|| format!("Unable to open file for writing: {path:?}"))?; - file.set_len(partition_len)?; - - let file = unsafe { MmapMut::map_mut(&file) } - .with_context(|| format!("Failed to mmap file: {path:?}"))?; - Ok(file) + if operation_needs_source(op_type) { + let source = source.as_ref().context("Source partition was not opened")?; + let src_ranges = extent_ranges(&op.src_extents, block_size, source.len) + .with_context(|| format!("Invalid source extents for operation {op_index}"))?; + ensure!(!src_ranges.is_empty(), "Operation {op_index} has no source extents"); + let src_len = ranges_len(&src_ranges)?; + if let Some(declared_len) = op.src_length { + ensure!( + usize::try_from(declared_len).ok() == Some(src_len), + "Source length mismatch for operation {op_index}" + ); + } + if self.verify { + verify_hash_over_ranges( + source.bytes(), + &src_ranges, + op.src_sha256_hash.as_deref(), + "Source extent", + ) + .with_context(|| { + format!("Source verification failed for operation {op_index}") + })?; + } + } + + if operation_has_data(op_type) { + let data = extract_operation_data(payload, op) + .with_context(|| format!("Invalid data for operation {op_index}"))?; + if self.verify { + verify_hash(data, op.data_sha256_hash.as_deref(), "Input").with_context( + || format!("Data verification failed for operation {op_index}"), + )?; + } + } + } + + destination_ranges.sort_unstable_by_key(|(range, _)| (range.start, range.end)); + for pair in destination_ranges.windows(2) { + let (left, left_op) = &pair[0]; + let (right, right_op) = &pair[1]; + ensure!( + left.end <= right.start, + "Destination extents overlap between operations {left_op} and {right_op}" + ); + } + + Ok(source) + } + + fn open_source_partition(&self, update: &PartitionUpdate) -> Result { + let source_dir = self.source_dir.as_ref().with_context(|| { + format!( + "Delta operations for partition {:?} require a source directory", + update.partition_name + ) + })?; + let path = source_dir.join(format!("{}.img", update.partition_name)); + let file = File::open(&path) + .with_context(|| format!("Unable to open source partition image: {path:?}"))?; + let metadata = file + .metadata() + .with_context(|| format!("Unable to inspect source partition image: {path:?}"))?; + ensure!(metadata.is_file(), "Source partition image is not a file: {path:?}"); + let len = usize::try_from(metadata.len()).context("Source partition is too large")?; + let mmap = if len == 0 { + None + } else { + Some( + unsafe { MmapOptions::new().map(&file) } + .with_context(|| format!("Failed to mmap source partition image: {path:?}"))?, + ) + }; + let source = SourcePartition { path, len, mmap }; + + let info = + update.old_partition_info.as_ref().context("Source partition info is missing")?; + let expected_size = info.size.context("Source partition size is missing")?; + ensure!( + usize::try_from(expected_size).ok() == Some(source.len), + "Source partition size mismatch for {:?}: expected {expected_size}, got {}", + update.partition_name, + source.len + ); + let expected_hash = info.hash.as_deref().context("Source partition hash is missing")?; + verify_hash(source.bytes(), Some(expected_hash), "Source partition")?; + Ok(source) + } + + fn create_staged_partition( + &self, + update: &PartitionUpdate, + staging_dir: &Path, + ) -> Result<(TempPath, MmapMut)> { + let partition_len = update + .new_partition_info + .as_ref() + .and_then(|info| info.size) + .context("Unable to determine output file size")?; + ensure!(partition_len > 0, "Output partition size must be greater than zero"); + + let file = tempfile::Builder::new() + .prefix("partition-") + .tempfile_in(staging_dir) + .context("Unable to create staged partition image")?; + file.as_file().set_len(partition_len)?; + + let partition = unsafe { MmapMut::map_mut(file.as_file()) } + .context("Failed to mmap staged partition image")?; + Ok((file.into_temp_path(), partition)) } /// Number of threads to use for extraction. By default, this is set to the @@ -280,11 +464,198 @@ impl<'a> ExtractOptions<'a> { self } - /// Verify the input and output partitions. This is enabled by default. + /// Verify operation data and source extent hashes. This is enabled by default. + /// Whole source images and staged output images are always verified. pub fn verify(&mut self, verify: bool) -> &mut Self { self.verify = verify; self } + + /// Set the directory containing base partition images for delta OTA extraction. + pub fn source_dir>(&mut self, source_dir: P) -> &mut Self { + self.source_dir = Some(source_dir.as_ref().to_path_buf()); + self + } +} + +struct SourcePartition { + path: PathBuf, + len: usize, + mmap: Option, +} + +impl SourcePartition { + fn bytes(&self) -> &[u8] { + self.mmap.as_deref().unwrap_or_default() + } +} + +fn validate_partition_name(name: &str) -> Result<()> { + let mut components = Path::new(name).components(); + let valid = matches!(components.next(), Some(Component::Normal(component)) if component == name) + && components.next().is_none() + && !name.contains('/') + && !name.contains('\\'); + ensure!(valid, "Unsafe partition name: {name:?}"); + Ok(()) +} + +#[allow(deprecated)] +fn validate_operation_type(op_type: Type) -> Result<()> { + match op_type { + Type::Move | Type::Bsdiff => bail!("Deprecated operation is not supported: {op_type:?}"), + Type::Replace + | Type::ReplaceBz + | Type::SourceCopy + | Type::SourceBsdiff + | Type::Zero + | Type::Discard + | Type::ReplaceXz + | Type::Puffdiff + | Type::BrotliBsdiff + | Type::Zucchini + | Type::Lz4diffBsdiff + | Type::Lz4diffPuffdiff + | Type::ReplaceZstd => Ok(()), + } +} + +fn operation_needs_source(op_type: Type) -> bool { + matches!( + op_type, + Type::SourceCopy + | Type::SourceBsdiff + | Type::Puffdiff + | Type::BrotliBsdiff + | Type::Zucchini + | Type::Lz4diffBsdiff + | Type::Lz4diffPuffdiff + ) +} + +fn operation_has_data(op_type: Type) -> bool { + matches!( + op_type, + Type::Replace + | Type::ReplaceBz + | Type::SourceBsdiff + | Type::ReplaceXz + | Type::Puffdiff + | Type::BrotliBsdiff + | Type::Zucchini + | Type::Lz4diffBsdiff + | Type::Lz4diffPuffdiff + | Type::ReplaceZstd + ) +} + +fn extent_ranges( + extents: &[chromeos_update_engine::Extent], + block_size: usize, + partition_len: usize, +) -> Result>> { + extents + .iter() + .map(|extent| { + let start_block = + usize::try_from(extent.start_block.context("start_block not defined in extent")?) + .context("Extent start block is too large")?; + let num_blocks = + usize::try_from(extent.num_blocks.context("num_blocks not defined in extent")?) + .context("Extent block count is too large")?; + let start = start_block.checked_mul(block_size).context("Extent offset overflow")?; + let len = num_blocks.checked_mul(block_size).context("Extent length overflow")?; + let end = start.checked_add(len).context("Extent end overflow")?; + ensure!(end <= partition_len, "Extent exceeds partition size"); + Ok(start..end) + }) + .collect() +} + +fn ranges_len(ranges: &[Range]) -> Result { + ranges.iter().try_fold(0usize, |len, range| { + len.checked_add(range.len()).context("Combined extent length overflow") + }) +} + +fn extract_operation_data<'a>(payload: &'a Payload<'a>, op: &InstallOperation) -> Result<&'a [u8]> { + let offset = usize::try_from(op.data_offset.context("data_offset not defined")?) + .context("Data offset is too large")?; + let len = usize::try_from(op.data_length.context("data_length not defined")?) + .context("Data length is too large")?; + let end = offset.checked_add(len).context("Data range overflow")?; + payload.data.get(offset..end).context("Data range exceeds payload size") +} + +fn verify_hash(data: &[u8], expected: Option<&[u8]>, label: &str) -> Result<()> { + let Some(expected) = expected else { + return Ok(()); + }; + let actual = digest::digest(&digest::SHA256, data); + ensure!( + actual.as_ref() == expected, + "{label} hash mismatch: expected {}, got {}", + hex::encode(expected), + hex::encode(actual.as_ref()) + ); + Ok(()) +} + +fn verify_hash_over_ranges( + data: &[u8], + ranges: &[Range], + expected: Option<&[u8]>, + label: &str, +) -> Result<()> { + let Some(expected) = expected else { + return Ok(()); + }; + let mut context = digest::Context::new(&digest::SHA256); + for range in ranges { + context.update(&data[range.clone()]); + } + let actual = context.finish(); + ensure!( + actual.as_ref() == expected, + "{label} hash mismatch: expected {}, got {}", + hex::encode(expected), + hex::encode(actual.as_ref()) + ); + Ok(()) +} + +fn paths_alias(source: &Path, destination: &Path) -> Result { + let destination_metadata = match fs::metadata(destination) { + Ok(metadata) => metadata, + Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(false), + Err(error) => { + return Err(error).with_context(|| { + format!("Unable to inspect destination partition image: {destination:?}") + }); + } + }; + let source_metadata = fs::metadata(source) + .with_context(|| format!("Unable to inspect source partition image: {source:?}"))?; + + #[cfg(unix)] + { + use std::os::unix::fs::MetadataExt as _; + Ok(source_metadata.dev() == destination_metadata.dev() + && source_metadata.ino() == destination_metadata.ino()) + } + #[cfg(not(unix))] + { + Ok(fs::canonicalize(source)? == fs::canonicalize(destination)?) + } +} + +fn path_entry_exists(path: &Path) -> Result { + match fs::symlink_metadata(path) { + Ok(_) => Ok(true), + Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(false), + Err(error) => Err(error) + .with_context(|| format!("Unable to inspect destination partition image: {path:?}")), + } } impl Default for ExtractOptions<'_> { @@ -299,6 +670,7 @@ struct Task<'a> { verify: bool, update: &'a PartitionUpdate, + source: Option, op_idx: AtomicUsize, partition: SyncUnsafeCell, @@ -335,6 +707,7 @@ impl Task<'_> { Ok(()) } + #[allow(deprecated)] fn run_op(&self, op: &InstallOperation) -> Result<()> { let mut dst_extents = self.extract_dst_extents(op).context("Error extracting dst_extents")?; @@ -363,11 +736,53 @@ impl Task<'_> { self.run_op_replace(&mut decoder, &mut dst_extents) .context("Error in REPLACE_ZSTD operation") } - Type::Zero => Ok(()), // This is a no-op since the partition is already zeroed + Type::SourceCopy => { + let source = self.extract_source_data(op)?; + self.write_exact(&source, &mut dst_extents) + .context("Error in SOURCE_COPY operation") + } + Type::Zero | Type::Discard => { + for extent in dst_extents { + extent.fill(0); + } + Ok(()) + } + Type::Move | Type::Bsdiff => { + bail!("Deprecated operation is not supported: {:?}", Type::try_from(op.r#type)?) + } op => bail!("Unimplemented operation: {op:?}"), } } + fn extract_source_data(&self, op: &InstallOperation) -> Result> { + let source = self.source.as_ref().context("Source partition was not opened")?; + let ranges = extent_ranges(&op.src_extents, self.block_size, source.len)?; + let capacity = ranges_len(&ranges)?; + let mut data = Vec::with_capacity(capacity); + for range in ranges { + data.extend_from_slice(&source.bytes()[range]); + } + Ok(data) + } + + fn write_exact(&self, data: &[u8], dst_extents: &mut [&mut [u8]]) -> Result<()> { + let dst_len = dst_extents.iter().try_fold(0usize, |len, extent| { + len.checked_add(extent.len()).context("Combined destination length overflow") + })?; + ensure!( + data.len() == dst_len, + "Patched output size mismatch: expected {dst_len}, got {}", + data.len() + ); + let mut offset = 0usize; + for extent in dst_extents { + let end = offset.checked_add(extent.len()).context("Destination offset overflow")?; + extent.copy_from_slice(&data[offset..end]); + offset = end; + } + Ok(()) + } + fn run_op_replace(&self, reader: &mut impl Read, dst_extents: &mut [&mut [u8]]) -> Result<()> { let dst_len = dst_extents.iter().map(|extent| extent.len()).sum::(); let mut bytes_written = 0usize; @@ -384,53 +799,38 @@ impl Task<'_> { } } } - ensure!(reader.read(&mut [0])? == 0, "Read fewer bytes than expected"); + ensure!(reader.read(&mut [0])? == 0, "Decoded data exceeds destination extents"); // Align number of bytes written to block size. The formula for alignment is: // ((operand + alignment - 1) / alignment) * alignment. - let bytes_written_aligned = bytes_written.div_ceil(self.block_size).mul(self.block_size); + let bytes_written_aligned = bytes_written + .checked_add(self.block_size - 1) + .context("Decoded data length overflow")? + / self.block_size + * self.block_size; ensure!(bytes_written_aligned == dst_len, "More dst blocks than data, even with padding"); Ok(()) } - fn extract_dst_extents(&self, op: &InstallOperation) -> Result> { + #[allow(clippy::mut_from_ref)] + fn extract_dst_extents(&self, op: &InstallOperation) -> Result> { let partition = unsafe { (*self.partition.get()).as_mut_ptr() }; let partition_len = unsafe { (&(*self.partition.get())).len() }; - - op.dst_extents - .iter() - .map(|extent| { - let start_block = - extent.start_block.context("start_block not defined in extent")? as usize; - let num_blocks = - extent.num_blocks.context("num_blocks not defined in extent")? as usize; - - let partition_offset = start_block * self.block_size; - let extent_len = num_blocks * self.block_size; - - ensure!( - partition_offset + extent_len <= partition_len, - "Extent exceeds partition size" - ); - let extent = unsafe { - slice::from_raw_parts_mut(partition.add(partition_offset), extent_len) - }; - - Ok(extent) + let ranges = extent_ranges(&op.dst_extents, self.block_size, partition_len)?; + + // validate_partition rejects overlapping destination ranges before any task starts. + // This guarantees that parallel workers never create aliases into the mutable map. + Ok(ranges + .into_iter() + .map(|range| unsafe { + slice::from_raw_parts_mut(partition.add(range.start), range.len()) }) - .collect() + .collect()) } fn extract_data<'a>(&'a self, op: &InstallOperation) -> Result<&'a [u8]> { - let data_len = op.data_length.context("data_length not defined")? as usize; - let data = { - let offset = op.data_offset.context("data_offset not defined")? as usize; - self.payload - .data - .get(offset..offset + data_len) - .context("Data offset exceeds payload size")? - }; + let data = extract_operation_data(self.payload, op)?; self.verify_op(op, data)?; Ok(data) } @@ -439,30 +839,16 @@ impl Task<'_> { if !self.verify { return Ok(()); } - let Some(exp_hash) = &op.data_sha256_hash else { - return Ok(()); - }; - - let got_hash = digest::digest(&digest::SHA256, data); - let got_hash = got_hash.as_ref(); - ensure!( - got_hash == exp_hash, - "Input verification failed: hash mismatch: expected {}, got {got_hash:?}", - hex::encode(exp_hash) - ); - Ok(()) + verify_hash(data, op.data_sha256_hash.as_deref(), "Input") } fn verify_partition(&self) -> Result<()> { - if !self.verify { - return Ok(()); - } - - let Some(exp_hash) = - self.update.new_partition_info.as_ref().and_then(|info| info.hash.as_ref()) - else { - return Ok(()); - }; + let exp_hash = self + .update + .new_partition_info + .as_ref() + .and_then(|info| info.hash.as_ref()) + .context("Unable to determine output partition hash")?; let mut digest = digest::Context::new(&digest::SHA256); for chunk in unsafe { (*self.partition.get()).chunks(VERIFY_CHUNK_SIZE) } { diff --git a/src/main.rs b/src/main.rs index 5c2e977..422332c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -39,6 +39,10 @@ pub struct Args { /// Path to the output directory #[clap(long)] pub output_dir: String, + + /// Directory containing source partition images for delta payloads + #[clap(long)] + pub source_dir: Option, } pub fn extract() { @@ -47,9 +51,12 @@ pub fn extract() { let reporter = Box::new(CliProgressReporter::new()); let reporter = reporter.as_ref(); - let result = ExtractOptions::new() - .progress_reporter(reporter) - .extract(&args.payload_file, &args.output_dir); + let mut options = ExtractOptions::new(); + options.progress_reporter(reporter); + if let Some(source_dir) = &args.source_dir { + options.source_dir(source_dir); + } + let result = options.extract(&args.payload_file, &args.output_dir); reporter.progress_bar.finish_and_clear(); match result { Ok(()) => { diff --git a/src/python.rs b/src/python.rs index 3173fb8..a0e81be 100644 --- a/src/python.rs +++ b/src/python.rs @@ -10,6 +10,7 @@ create_exception!(otadump, OtaDumpError, PyException); /// Extract partitions from an Android OTA payload or OTA ZIP. #[pyfunction] +#[allow(clippy::too_many_arguments)] #[pyo3(signature = ( payload_file, output_dir, @@ -18,6 +19,7 @@ create_exception!(otadump, OtaDumpError, PyException); overwrite = false, partitions = None, verify = true, + source_dir = None, ))] fn extract( py: Python<'_>, @@ -27,6 +29,7 @@ fn extract( overwrite: bool, partitions: Option>, verify: bool, + source_dir: Option, ) -> PyResult<()> { let mut options = ExtractOptions::new(); options.overwrite(overwrite).verify(verify); @@ -37,6 +40,9 @@ fn extract( if let Some(partitions) = partitions { options.partitions(partitions); } + if let Some(source_dir) = source_dir { + options.source_dir(source_dir); + } py.detach(move || { options diff --git a/tests/delta_extraction.rs b/tests/delta_extraction.rs new file mode 100644 index 0000000..878d5a1 --- /dev/null +++ b/tests/delta_extraction.rs @@ -0,0 +1,520 @@ +use std::fs; +use std::path::Path; + +use otadump::ExtractOptions; +use prost::Message; +use ring::digest; +use tempfile::TempDir; + +#[derive(Clone, PartialEq, Message)] +struct Extent { + #[prost(uint64, optional, tag = "1")] + start_block: Option, + #[prost(uint64, optional, tag = "2")] + num_blocks: Option, +} + +#[derive(Clone, PartialEq, Message)] +struct PartitionInfo { + #[prost(uint64, optional, tag = "1")] + size: Option, + #[prost(bytes = "vec", optional, tag = "2")] + hash: Option>, +} + +#[derive(Clone, PartialEq, Message)] +struct InstallOperation { + #[prost(int32, tag = "1")] + operation_type: i32, + #[prost(uint64, optional, tag = "2")] + data_offset: Option, + #[prost(uint64, optional, tag = "3")] + data_length: Option, + #[prost(message, repeated, tag = "4")] + src_extents: Vec, + #[prost(uint64, optional, tag = "5")] + src_length: Option, + #[prost(message, repeated, tag = "6")] + dst_extents: Vec, + #[prost(uint64, optional, tag = "7")] + dst_length: Option, + #[prost(bytes = "vec", optional, tag = "8")] + data_sha256_hash: Option>, + #[prost(bytes = "vec", optional, tag = "9")] + src_sha256_hash: Option>, +} + +#[derive(Clone, PartialEq, Message)] +struct PartitionUpdate { + #[prost(string, tag = "1")] + partition_name: String, + #[prost(message, optional, tag = "6")] + old_partition_info: Option, + #[prost(message, optional, tag = "7")] + new_partition_info: Option, + #[prost(message, repeated, tag = "8")] + operations: Vec, +} + +#[derive(Clone, PartialEq, Message)] +struct DeltaArchiveManifest { + #[prost(uint32, optional, tag = "3")] + block_size: Option, + #[prost(uint32, optional, tag = "12")] + minor_version: Option, + #[prost(message, repeated, tag = "13")] + partitions: Vec, +} + +fn sha256(bytes: &[u8]) -> Vec { + digest::digest(&digest::SHA256, bytes).as_ref().to_vec() +} + +fn extent(start_block: u64, num_blocks: u64) -> Extent { + Extent { start_block: Some(start_block), num_blocks: Some(num_blocks) } +} + +fn write_payload(path: &Path, manifest: DeltaArchiveManifest, data: &[u8]) { + let manifest = manifest.encode_to_vec(); + let mut payload = Vec::with_capacity(24 + manifest.len() + data.len()); + payload.extend_from_slice(b"CrAU"); + payload.extend_from_slice(&2u64.to_be_bytes()); + payload.extend_from_slice(&(manifest.len() as u64).to_be_bytes()); + payload.extend_from_slice(&0u32.to_be_bytes()); + payload.extend_from_slice(&manifest); + payload.extend_from_slice(data); + fs::write(path, payload).unwrap(); +} + +fn partition_info(bytes: &[u8]) -> PartitionInfo { + PartitionInfo { size: Some(bytes.len() as u64), hash: Some(sha256(bytes)) } +} + +#[test] +fn source_copy_reconstructs_noncontiguous_extents_without_modifying_source() { + let temporary = TempDir::new().unwrap(); + let source_dir = temporary.path().join("source"); + let output_dir = temporary.path().join("output"); + fs::create_dir(&source_dir).unwrap(); + let source = b"AAAABBBBCCCCDDDD"; + fs::write(source_dir.join("system.img"), source).unwrap(); + let expected = b"AAAACCCC"; + let operation = InstallOperation { + operation_type: 4, + src_extents: vec![extent(2, 1), extent(0, 1)], + src_length: Some(8), + dst_extents: vec![extent(1, 1), extent(0, 1)], + dst_length: Some(8), + src_sha256_hash: Some(sha256(b"CCCCAAAA")), + ..Default::default() + }; + let manifest = DeltaArchiveManifest { + block_size: Some(4), + minor_version: Some(2), + partitions: vec![PartitionUpdate { + partition_name: "system".into(), + old_partition_info: Some(partition_info(source)), + new_partition_info: Some(partition_info(expected)), + operations: vec![operation], + }], + }; + let payload = temporary.path().join("payload.bin"); + write_payload(&payload, manifest, &[]); + + ExtractOptions::new() + .source_dir(&source_dir) + .num_threads(2) + .extract(&payload, &output_dir) + .unwrap(); + + assert_eq!(fs::read(output_dir.join("system.img")).unwrap(), expected); + assert_eq!(fs::read(source_dir.join("system.img")).unwrap(), source); +} + +#[test] +fn source_validation_rejects_wrong_base_before_creating_output() { + let temporary = TempDir::new().unwrap(); + let source_dir = temporary.path().join("source"); + let output_dir = temporary.path().join("output"); + fs::create_dir(&source_dir).unwrap(); + fs::write(source_dir.join("boot.img"), b"bad!").unwrap(); + let operation = InstallOperation { + operation_type: 4, + src_extents: vec![extent(0, 1)], + dst_extents: vec![extent(0, 1)], + ..Default::default() + }; + let manifest = DeltaArchiveManifest { + block_size: Some(4), + minor_version: Some(2), + partitions: vec![PartitionUpdate { + partition_name: "boot".into(), + old_partition_info: Some(partition_info(b"good")), + new_partition_info: Some(partition_info(b"good")), + operations: vec![operation], + }], + }; + let payload = temporary.path().join("payload.bin"); + write_payload(&payload, manifest, &[]); + + let error = ExtractOptions::new() + .source_dir(&source_dir) + .extract(&payload, &output_dir) + .unwrap_err() + .to_string(); + + assert!(error.contains("Source partition hash mismatch")); + assert!(!output_dir.join("boot.img").exists()); +} + +#[test] +fn delta_operation_without_source_directory_fails_clearly() { + let temporary = TempDir::new().unwrap(); + let output_dir = temporary.path().join("output"); + let manifest = DeltaArchiveManifest { + block_size: Some(4), + minor_version: Some(2), + partitions: vec![PartitionUpdate { + partition_name: "boot".into(), + old_partition_info: Some(partition_info(b"base")), + new_partition_info: Some(partition_info(b"base")), + operations: vec![InstallOperation { + operation_type: 4, + src_extents: vec![extent(0, 1)], + dst_extents: vec![extent(0, 1)], + ..Default::default() + }], + }], + }; + let payload = temporary.path().join("payload.bin"); + write_payload(&payload, manifest, &[]); + + let error = ExtractOptions::new().extract(&payload, &output_dir).unwrap_err().to_string(); + + assert!(error.contains("require a source directory")); + assert!(!output_dir.join("boot.img").exists()); +} + +#[test] +fn malformed_partition_layouts_fail_without_escaping_or_leaving_partial_images() { + for (name, operations, expected_error) in [ + ( + "../escape", + vec![InstallOperation { + operation_type: 0, + data_offset: Some(0), + data_length: Some(4), + dst_extents: vec![extent(0, 1)], + ..Default::default() + }], + "Unsafe partition name", + ), + ( + "system", + vec![ + InstallOperation { + operation_type: 0, + data_offset: Some(0), + data_length: Some(4), + dst_extents: vec![extent(0, 1)], + ..Default::default() + }, + InstallOperation { + operation_type: 0, + data_offset: Some(4), + data_length: Some(4), + dst_extents: vec![extent(0, 1)], + ..Default::default() + }, + ], + "Destination extents overlap", + ), + ] { + let temporary = TempDir::new().unwrap(); + let output_dir = temporary.path().join("output"); + let target = b"data"; + let manifest = DeltaArchiveManifest { + block_size: Some(4), + minor_version: Some(0), + partitions: vec![PartitionUpdate { + partition_name: name.into(), + old_partition_info: None, + new_partition_info: Some(partition_info(target)), + operations, + }], + }; + let payload = temporary.path().join("payload.bin"); + write_payload(&payload, manifest, b"datadata"); + + let error = ExtractOptions::new().extract(&payload, &output_dir).unwrap_err().to_string(); + + assert!(error.contains(expected_error), "unexpected error: {error}"); + assert!(!temporary.path().join("escape.img").exists()); + assert!(!output_dir.join("system.img").exists()); + } +} + +#[test] +fn full_replace_payload_remains_compatible() { + let temporary = TempDir::new().unwrap(); + let output_dir = temporary.path().join("output"); + let expected = b"full payload data"; + let operation = InstallOperation { + operation_type: 0, + data_offset: Some(0), + data_length: Some(expected.len() as u64), + dst_extents: vec![extent(0, 2)], + data_sha256_hash: Some(sha256(expected)), + ..Default::default() + }; + let mut padded = expected.to_vec(); + padded.resize(32, 0); + let manifest = DeltaArchiveManifest { + block_size: Some(16), + minor_version: Some(0), + partitions: vec![PartitionUpdate { + partition_name: "vendor".into(), + old_partition_info: None, + new_partition_info: Some(partition_info(&padded)), + operations: vec![operation], + }], + }; + let payload = temporary.path().join("payload.bin"); + write_payload(&payload, manifest, expected); + + ExtractOptions::new().extract(&payload, &output_dir).unwrap(); + + assert_eq!(fs::read(output_dir.join("vendor.img")).unwrap(), padded); +} + +#[test] +fn verified_partitions_are_promoted_after_all_succeed() { + let temporary = TempDir::new().unwrap(); + let output_dir = temporary.path().join("output"); + let boot = b"boot"; + let vendor = b"vend"; + let manifest = DeltaArchiveManifest { + block_size: Some(4), + minor_version: Some(0), + partitions: vec![ + PartitionUpdate { + partition_name: "boot".into(), + old_partition_info: None, + new_partition_info: Some(partition_info(boot)), + operations: vec![InstallOperation { + operation_type: 0, + data_offset: Some(0), + data_length: Some(4), + dst_extents: vec![extent(0, 1)], + ..Default::default() + }], + }, + PartitionUpdate { + partition_name: "vendor".into(), + old_partition_info: None, + new_partition_info: Some(partition_info(vendor)), + operations: vec![InstallOperation { + operation_type: 0, + data_offset: Some(4), + data_length: Some(4), + dst_extents: vec![extent(0, 1)], + ..Default::default() + }], + }, + ], + }; + let payload = temporary.path().join("payload.bin"); + write_payload(&payload, manifest, b"bootvend"); + + ExtractOptions::new().extract(&payload, &output_dir).unwrap(); + + assert_eq!(fs::read(output_dir.join("boot.img")).unwrap(), boot); + assert_eq!(fs::read(output_dir.join("vendor.img")).unwrap(), vendor); +} + +#[test] +fn no_clobber_rejects_existing_destination_without_modifying_source() { + let temporary = TempDir::new().unwrap(); + let source_dir = temporary.path().join("source"); + let output_dir = temporary.path().join("output"); + fs::create_dir(&source_dir).unwrap(); + fs::create_dir(&output_dir).unwrap(); + let source = b"base"; + let existing = b"keep"; + fs::write(source_dir.join("system.img"), source).unwrap(); + fs::write(output_dir.join("system.img"), existing).unwrap(); + let manifest = DeltaArchiveManifest { + block_size: Some(4), + minor_version: Some(2), + partitions: vec![PartitionUpdate { + partition_name: "system".into(), + old_partition_info: Some(partition_info(source)), + new_partition_info: Some(partition_info(source)), + operations: vec![InstallOperation { + operation_type: 4, + src_extents: vec![extent(0, 1)], + dst_extents: vec![extent(0, 1)], + ..Default::default() + }], + }], + }; + let payload = temporary.path().join("payload.bin"); + write_payload(&payload, manifest, &[]); + + let error = ExtractOptions::new() + .source_dir(&source_dir) + .extract(&payload, &output_dir) + .unwrap_err() + .to_string(); + + assert!(error.contains("already exists")); + assert_eq!(fs::read(output_dir.join("system.img")).unwrap(), existing); + assert_eq!(fs::read(source_dir.join("system.img")).unwrap(), source); + assert_eq!(fs::read_dir(&output_dir).unwrap().count(), 1); +} + +#[test] +fn verification_failure_preserves_existing_destinations_with_overwrite() { + let temporary = TempDir::new().unwrap(); + let output_dir = temporary.path().join("output"); + fs::create_dir(&output_dir).unwrap(); + fs::write(output_dir.join("boot.img"), b"oldb").unwrap(); + fs::write(output_dir.join("vendor.img"), b"oldv").unwrap(); + let manifest = DeltaArchiveManifest { + block_size: Some(4), + minor_version: Some(0), + partitions: vec![ + PartitionUpdate { + partition_name: "boot".into(), + old_partition_info: None, + new_partition_info: Some(partition_info(b"boot")), + operations: vec![InstallOperation { + operation_type: 0, + data_offset: Some(0), + data_length: Some(4), + dst_extents: vec![extent(0, 1)], + ..Default::default() + }], + }, + PartitionUpdate { + partition_name: "vendor".into(), + old_partition_info: None, + new_partition_info: Some(partition_info(b"bad!")), + operations: vec![InstallOperation { + operation_type: 0, + data_offset: Some(4), + data_length: Some(4), + dst_extents: vec![extent(0, 1)], + ..Default::default() + }], + }, + ], + }; + let payload = temporary.path().join("payload.bin"); + write_payload(&payload, manifest, b"bootvend"); + + let error = ExtractOptions::new() + .overwrite(true) + .extract(&payload, &output_dir) + .unwrap_err() + .to_string(); + + assert!(error.contains("Output verification failed")); + assert_eq!(fs::read(output_dir.join("boot.img")).unwrap(), b"oldb"); + assert_eq!(fs::read(output_dir.join("vendor.img")).unwrap(), b"oldv"); + assert_eq!(fs::read_dir(&output_dir).unwrap().count(), 2); +} + +#[test] +fn partition_selection_does_not_require_unselected_delta_sources() { + let temporary = TempDir::new().unwrap(); + let output_dir = temporary.path().join("output"); + let expected = b"boot"; + let manifest = DeltaArchiveManifest { + block_size: Some(4), + minor_version: Some(2), + partitions: vec![ + PartitionUpdate { + partition_name: "boot".into(), + old_partition_info: None, + new_partition_info: Some(partition_info(expected)), + operations: vec![InstallOperation { + operation_type: 0, + data_offset: Some(0), + data_length: Some(4), + dst_extents: vec![extent(0, 1)], + ..Default::default() + }], + }, + PartitionUpdate { + partition_name: "system".into(), + old_partition_info: Some(partition_info(b"base")), + new_partition_info: Some(partition_info(b"base")), + operations: vec![InstallOperation { + operation_type: 4, + src_extents: vec![extent(0, 1)], + dst_extents: vec![extent(0, 1)], + ..Default::default() + }], + }, + ], + }; + let payload = temporary.path().join("payload.bin"); + write_payload(&payload, manifest, expected); + + ExtractOptions::new().partitions(["boot"]).extract(&payload, &output_dir).unwrap(); + + assert_eq!(fs::read(output_dir.join("boot.img")).unwrap(), expected); + assert!(!output_dir.join("system.img").exists()); +} + +#[cfg(unix)] +#[test] +fn source_and_destination_aliases_are_rejected_without_modifying_the_source() { + use std::os::unix::fs::symlink; + + for link in ["hard", "symbolic"] { + let temporary = TempDir::new().unwrap(); + let source_dir = temporary.path().join("source"); + let output_dir = temporary.path().join("output"); + fs::create_dir(&source_dir).unwrap(); + fs::create_dir(&output_dir).unwrap(); + let source_path = source_dir.join("system.img"); + let source = b"base"; + fs::write(&source_path, source).unwrap(); + let destination_path = output_dir.join("system.img"); + if link == "hard" { + fs::hard_link(&source_path, &destination_path).unwrap(); + } else { + symlink(&source_path, &destination_path).unwrap(); + } + + let manifest = DeltaArchiveManifest { + block_size: Some(4), + minor_version: Some(2), + partitions: vec![PartitionUpdate { + partition_name: "system".into(), + old_partition_info: Some(partition_info(source)), + new_partition_info: Some(partition_info(source)), + operations: vec![InstallOperation { + operation_type: 4, + src_extents: vec![extent(0, 1)], + dst_extents: vec![extent(0, 1)], + ..Default::default() + }], + }], + }; + let payload = temporary.path().join("payload.bin"); + write_payload(&payload, manifest, &[]); + + let error = ExtractOptions::new() + .source_dir(&source_dir) + .overwrite(true) + .extract(&payload, &output_dir) + .unwrap_err() + .to_string(); + + assert!(error.contains("refer to the same file")); + assert_eq!(fs::read(&source_path).unwrap(), source); + } +} diff --git a/tests/python_smoke.py b/tests/python_smoke.py index 8c9d270..d32ba71 100644 --- a/tests/python_smoke.py +++ b/tests/python_smoke.py @@ -7,7 +7,9 @@ def main() -> None: assert issubclass(otadump.OtaDumpError, Exception) - assert "partitions" in str(signature(otadump.extract)) + extract_signature = str(signature(otadump.extract)) + assert "partitions" in extract_signature + assert "source_dir=None" in extract_signature with TemporaryDirectory() as temporary_directory: temporary_path = Path(temporary_directory) From 9e9f912f3b22218892f9fc0d0eeb6ee3ec3d7661 Mon Sep 17 00:00:00 2001 From: Omkar Chandorkar Date: Fri, 11 Sep 2026 13:21:36 +0530 Subject: [PATCH 02/42] extractor: Add graceful cancellation Signed-off-by: Omkar Chandorkar --- Cargo.lock | 85 +++++++++++++++++- Cargo.toml | 1 + src/lib.rs | 175 ++++++++++++++++++++++++++++++++------ src/main.rs | 21 +++-- src/python.rs | 64 ++++++++++++-- tests/delta_extraction.rs | 159 +++++++++++++++++++++++++++++++++- tests/python_smoke.py | 16 ++++ 7 files changed, 480 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 370ea0e..6ed9355 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,6 +82,15 @@ version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + [[package]] name = "bumpalo" version = "3.18.1" @@ -120,6 +129,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +[[package]] +name = "cfg_aliases" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" + [[package]] name = "clap" version = "4.5.40" @@ -234,6 +249,17 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "ctrlc" +version = "3.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0b1fab2ae45819af2d0731d60f2afe17227ebb1a1538a236da84c93e9a60162" +dependencies = [ + "dispatch2", + "nix", + "windows-sys 0.61.2", +] + [[package]] name = "derive_arbitrary" version = "1.4.1" @@ -245,6 +271,18 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags", + "block2", + "libc", + "objc2", +] + [[package]] name = "either" version = "1.15.0" @@ -392,9 +430,9 @@ checksum = "775bf80d5878ab7c2b1080b5351a48b2f737d9f6f8b383574eebcc22be0dfccb" [[package]] name = "libc" -version = "0.2.174" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "liblzma" @@ -455,6 +493,18 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" +[[package]] +name = "nix" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "nom" version = "7.1.3" @@ -493,6 +543,21 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + [[package]] name = "once_cell" version = "1.21.3" @@ -513,6 +578,7 @@ dependencies = [ "bzip2", "clap", "color-print", + "ctrlc", "hex", "indicatif", "liblzma", @@ -1007,6 +1073,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + [[package]] name = "windows-sys" version = "0.52.0" @@ -1034,6 +1106,15 @@ dependencies = [ "windows-targets 0.53.2", ] +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-targets" version = "0.52.6" diff --git a/Cargo.toml b/Cargo.toml index 542c789..e5e946f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ anyhow = "1.0.69" bzip2 = { version = "0.6.0" } clap = { version = "4.2.1", features = ["derive"] } color-print = "0.3.4" +ctrlc = "3.5.0" hex = "0.4.3" indicatif = "0.17.3" liblzma = { version = "0.4.2", features = ["static"] } diff --git a/src/lib.rs b/src/lib.rs index 87ea92a..a8b8c75 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,8 +14,8 @@ use std::io::Read; use std::num::NonZero; use std::ops::Range; use std::path::{Component, Path, PathBuf}; -use std::sync::OnceLock; -use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use std::sync::{Arc, OnceLock}; use std::{error, result, slice, thread}; use anyhow::{Context as _, Error, Result, bail, ensure}; @@ -39,6 +39,54 @@ pub use crate::payload::Payload; // the progress tracker. const VERIFY_CHUNK_SIZE: usize = 2 * 1024 * 1024; // 2 MiB +#[derive(Clone, Debug, Default)] +pub struct CancellationToken { + cancelled: Arc, +} + +impl CancellationToken { + pub fn new() -> Self { + Self::default() + } + + pub fn cancel(&self) { + self.cancelled.store(true, Ordering::Release); + } + + pub fn is_cancelled(&self) -> bool { + self.cancelled.load(Ordering::Acquire) + } + + fn check(&self) -> Result<()> { + if self.is_cancelled() { + return Err(ExtractionCancelled.into()); + } + Ok(()) + } +} + +#[derive(Debug)] +pub struct ExtractionCancelled; + +impl std::fmt::Display for ExtractionCancelled { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("Extraction cancelled") + } +} + +impl error::Error for ExtractionCancelled {} + +pub fn is_cancellation(error: &(dyn error::Error + 'static)) -> bool { + let mut error = Some(error); + while let Some(current) = error { + if current.downcast_ref::().is_some() { + return true; + } + error = current.source(); + } + false +} + pub struct ExtractOptions<'a> { num_threads: Option, overwrite: bool, @@ -46,6 +94,7 @@ pub struct ExtractOptions<'a> { progress_reporter: &'a dyn ProgressReporter, verify: bool, source_dir: Option, + cancellation_token: CancellationToken, } impl<'a> ExtractOptions<'a> { @@ -58,6 +107,7 @@ impl<'a> ExtractOptions<'a> { progress_reporter: &NoOpProgressReporter, verify: true, source_dir: None, + cancellation_token: CancellationToken::new(), } } @@ -73,14 +123,18 @@ impl<'a> ExtractOptions<'a> { { let payload_file = payload_file.as_ref(); let output_dir = output_dir.as_ref(); - self.extract_impl(payload_file, output_dir)?; - Ok(()) + match self.extract_impl(payload_file, output_dir) { + Ok(()) => Ok(()), + Err(error) if error.is::() => Err(Box::new(ExtractionCancelled)), + Err(error) => Err(error.into()), + } } fn extract_impl(&self, payload_file: &Path, output_dir: &Path) -> Result<()> { + self.cancellation_token.check()?; self.progress_reporter.report_progress(0.); - let payload_file = Self::open_payload_file(payload_file)?; + let payload_file = self.open_payload_file(payload_file)?; let payload = Payload::parse(&payload_file)?; let mut manifest = @@ -118,6 +172,7 @@ impl<'a> ExtractOptions<'a> { // Ensure that all partitions to be extracted are present in the manifest. for partition_name in self.partitions.iter().flatten() { + self.cancellation_token.check()?; ensure!( manifest.partitions.iter().any(|update| &update.partition_name == partition_name), "Partition not found: {partition_name}", @@ -136,6 +191,7 @@ impl<'a> ExtractOptions<'a> { let mut partition_names = HashSet::with_capacity(manifest.partitions.len()); let mut destinations = Vec::with_capacity(manifest.partitions.len()); for update in &manifest.partitions { + self.cancellation_token.check()?; ensure!( partition_names.insert(&update.partition_name), "Duplicate partition mapping: {:?}", @@ -144,6 +200,7 @@ impl<'a> ExtractOptions<'a> { destinations.push(output_dir.join(format!("{}.img", update.partition_name))); } for (update, destination) in manifest.partitions.iter().zip(&destinations) { + self.cancellation_token.check()?; for source in sources.iter().flatten() { ensure!( !paths_alias(&source.path, destination)?, @@ -167,6 +224,7 @@ impl<'a> ExtractOptions<'a> { let mut staging_paths = Vec::with_capacity(manifest.partitions.len()); let mut partitions = Vec::with_capacity(manifest.partitions.len()); for update in &manifest.partitions { + self.cancellation_token.check()?; let (path, partition) = self.create_staged_partition(update, staging_dir.path())?; staging_paths.push(path); partitions.push(partition); @@ -190,6 +248,10 @@ impl<'a> ExtractOptions<'a> { if error.get().is_some() { break; } + if let Err(cancelled) = self.cancellation_token.check() { + _ = error.set(cancelled); + break; + } // Create and broadcast a task to the threadpool. let task = Task { @@ -203,6 +265,7 @@ impl<'a> ExtractOptions<'a> { total_ops, total_ops_completed: &total_ops_completed, progress_reporter: self.progress_reporter, + cancellation_token: &self.cancellation_token, error: &error, }; scope.spawn_broadcast(move |_, ctx| { @@ -213,11 +276,13 @@ impl<'a> ExtractOptions<'a> { } }); + self.cancellation_token.check()?; if let Some(e) = error.take() { return Err(e); } for (staging_path, destination) in staging_paths.into_iter().zip(destinations) { + self.cancellation_token.check()?; let result = if self.overwrite { staging_path.persist(&destination) } else { @@ -227,10 +292,11 @@ impl<'a> ExtractOptions<'a> { } self.progress_reporter.report_progress(1.); + self.cancellation_token.check()?; Ok(()) } - fn open_payload_file(path: &Path) -> Result { + fn open_payload_file(&self, path: &Path) -> Result { let in_file = File::open(path) .with_context(|| format!("Failed to open file for reading: {path:?}"))?; @@ -261,9 +327,12 @@ impl<'a> ExtractOptions<'a> { let mut out_file = unsafe { MmapMut::map_mut(&out_file) } .context("Failed to mmap temporary file")?; - zip_file - .read_exact(&mut out_file) - .context("Failed to write to temporary file")?; + for chunk in out_file.chunks_mut(VERIFY_CHUNK_SIZE) { + self.cancellation_token.check()?; + zip_file + .read_exact(chunk) + .context("Failed to write to temporary file")?; + } out_file.make_read_only().context("Failed to make temporary file read-only") } } @@ -281,6 +350,7 @@ impl<'a> ExtractOptions<'a> { block_size: usize, ) -> Result> { validate_partition_name(&update.partition_name)?; + self.cancellation_token.check()?; let partition_len = update .new_partition_info @@ -295,6 +365,7 @@ impl<'a> ExtractOptions<'a> { ); let needs_source = update.operations.iter().try_fold(false, |needed, op| { + self.cancellation_token.check()?; let op_type = Type::try_from(op.r#type).context("Invalid operation")?; validate_operation_type(op_type)?; Ok::<_, Error>(needed || operation_needs_source(op_type)) @@ -303,6 +374,7 @@ impl<'a> ExtractOptions<'a> { let mut destination_ranges = Vec::new(); for (op_index, op) in update.operations.iter().enumerate() { + self.cancellation_token.check()?; let op_type = Type::try_from(op.r#type).context("Invalid operation")?; let dst_ranges = extent_ranges(&op.dst_extents, block_size, partition_len) .with_context(|| format!("Invalid destination extents for operation {op_index}"))?; @@ -339,6 +411,7 @@ impl<'a> ExtractOptions<'a> { &src_ranges, op.src_sha256_hash.as_deref(), "Source extent", + &self.cancellation_token, ) .with_context(|| { format!("Source verification failed for operation {op_index}") @@ -350,9 +423,15 @@ impl<'a> ExtractOptions<'a> { let data = extract_operation_data(payload, op) .with_context(|| format!("Invalid data for operation {op_index}"))?; if self.verify { - verify_hash(data, op.data_sha256_hash.as_deref(), "Input").with_context( - || format!("Data verification failed for operation {op_index}"), - )?; + verify_hash( + data, + op.data_sha256_hash.as_deref(), + "Input", + &self.cancellation_token, + ) + .with_context(|| { + format!("Data verification failed for operation {op_index}") + })?; } } } @@ -405,7 +484,12 @@ impl<'a> ExtractOptions<'a> { source.len ); let expected_hash = info.hash.as_deref().context("Source partition hash is missing")?; - verify_hash(source.bytes(), Some(expected_hash), "Source partition")?; + verify_hash( + source.bytes(), + Some(expected_hash), + "Source partition", + &self.cancellation_token, + )?; Ok(source) } @@ -476,6 +560,12 @@ impl<'a> ExtractOptions<'a> { self.source_dir = Some(source_dir.as_ref().to_path_buf()); self } + + /// Stop extraction cooperatively when the caller cancels the token. + pub fn cancellation_token(&mut self, cancellation_token: &CancellationToken) -> &mut Self { + self.cancellation_token = cancellation_token.clone(); + self + } } struct SourcePartition { @@ -587,11 +677,22 @@ fn extract_operation_data<'a>(payload: &'a Payload<'a>, op: &InstallOperation) - payload.data.get(offset..end).context("Data range exceeds payload size") } -fn verify_hash(data: &[u8], expected: Option<&[u8]>, label: &str) -> Result<()> { +fn verify_hash( + data: &[u8], + expected: Option<&[u8]>, + label: &str, + cancellation_token: &CancellationToken, +) -> Result<()> { + cancellation_token.check()?; let Some(expected) = expected else { return Ok(()); }; - let actual = digest::digest(&digest::SHA256, data); + let mut context = digest::Context::new(&digest::SHA256); + for chunk in data.chunks(VERIFY_CHUNK_SIZE) { + cancellation_token.check()?; + context.update(chunk); + } + let actual = context.finish(); ensure!( actual.as_ref() == expected, "{label} hash mismatch: expected {}, got {}", @@ -606,13 +707,18 @@ fn verify_hash_over_ranges( ranges: &[Range], expected: Option<&[u8]>, label: &str, + cancellation_token: &CancellationToken, ) -> Result<()> { + cancellation_token.check()?; let Some(expected) = expected else { return Ok(()); }; let mut context = digest::Context::new(&digest::SHA256); for range in ranges { - context.update(&data[range.clone()]); + for chunk in data[range.clone()].chunks(VERIFY_CHUNK_SIZE) { + cancellation_token.check()?; + context.update(chunk); + } } let actual = context.finish(); ensure!( @@ -677,6 +783,7 @@ struct Task<'a> { total_ops: usize, total_ops_completed: &'a AtomicUsize, progress_reporter: &'a dyn ProgressReporter, + cancellation_token: &'a CancellationToken, error: &'a OnceLock, } @@ -685,10 +792,12 @@ impl Task<'_> { fn run(&self, ctx: BroadcastContext<'_>) -> Result<()> { // If an error has already occurred, stop processing the partition. while self.error.get().is_none() { + self.cancellation_token.check()?; let op_idx = self.op_idx.fetch_add(1, Ordering::AcqRel); match self.update.operations.get(op_idx) { Some(op) => { self.run_op(op)?; + self.cancellation_token.check()?; self.increment_progress(); } None => { @@ -696,7 +805,9 @@ impl Task<'_> { // partition, the partition is fully extracted and can now be // verified. if op_idx + 1 == self.update.operations.len() + ctx.num_threads() { + self.cancellation_token.check()?; self.verify_partition()?; + self.cancellation_token.check()?; unsafe { (*self.partition.get()).flush() } .context("Error while flushing file to disk")?; }; @@ -709,6 +820,7 @@ impl Task<'_> { #[allow(deprecated)] fn run_op(&self, op: &InstallOperation) -> Result<()> { + self.cancellation_token.check()?; let mut dst_extents = self.extract_dst_extents(op).context("Error extracting dst_extents")?; match Type::try_from(op.r#type).context("Invalid operation")? { @@ -743,7 +855,10 @@ impl Task<'_> { } Type::Zero | Type::Discard => { for extent in dst_extents { - extent.fill(0); + for chunk in extent.chunks_mut(VERIFY_CHUNK_SIZE) { + self.cancellation_token.check()?; + chunk.fill(0); + } } Ok(()) } @@ -751,7 +866,8 @@ impl Task<'_> { bail!("Deprecated operation is not supported: {:?}", Type::try_from(op.r#type)?) } op => bail!("Unimplemented operation: {op:?}"), - } + }?; + self.cancellation_token.check() } fn extract_source_data(&self, op: &InstallOperation) -> Result> { @@ -760,7 +876,10 @@ impl Task<'_> { let capacity = ranges_len(&ranges)?; let mut data = Vec::with_capacity(capacity); for range in ranges { - data.extend_from_slice(&source.bytes()[range]); + for chunk in source.bytes()[range].chunks(VERIFY_CHUNK_SIZE) { + self.cancellation_token.check()?; + data.extend_from_slice(chunk); + } } Ok(data) } @@ -776,9 +895,12 @@ impl Task<'_> { ); let mut offset = 0usize; for extent in dst_extents { - let end = offset.checked_add(extent.len()).context("Destination offset overflow")?; - extent.copy_from_slice(&data[offset..end]); - offset = end; + for chunk in extent.chunks_mut(VERIFY_CHUNK_SIZE) { + self.cancellation_token.check()?; + let end = offset.checked_add(chunk.len()).context("Destination offset overflow")?; + chunk.copy_from_slice(&data[offset..end]); + offset = end; + } } Ok(()) } @@ -790,7 +912,9 @@ impl Task<'_> { for extent in dst_extents { let mut extent = &mut **extent; loop { - match reader.read(extent).context("Failed to write to buffer")? { + self.cancellation_token.check()?; + let chunk_len = extent.len().min(VERIFY_CHUNK_SIZE); + match reader.read(&mut extent[..chunk_len]).context("Failed to write to buffer")? { 0 => break, n => { bytes_written += n; @@ -799,6 +923,7 @@ impl Task<'_> { } } } + self.cancellation_token.check()?; ensure!(reader.read(&mut [0])? == 0, "Decoded data exceeds destination extents"); // Align number of bytes written to block size. The formula for alignment is: @@ -839,7 +964,7 @@ impl Task<'_> { if !self.verify { return Ok(()); } - verify_hash(data, op.data_sha256_hash.as_deref(), "Input") + verify_hash(data, op.data_sha256_hash.as_deref(), "Input", self.cancellation_token) } fn verify_partition(&self) -> Result<()> { @@ -852,10 +977,12 @@ impl Task<'_> { let mut digest = digest::Context::new(&digest::SHA256); for chunk in unsafe { (*self.partition.get()).chunks(VERIFY_CHUNK_SIZE) } { + self.cancellation_token.check()?; digest.update(chunk); self.increment_progress(); } + self.cancellation_token.check()?; let got_hash = digest.finish(); let got_hash = got_hash.as_ref(); ensure!( diff --git a/src/main.rs b/src/main.rs index 422332c..6a72c93 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,14 @@ -use anyhow::Result; use clap::Parser; use indicatif::{ProgressBar, ProgressStyle}; -use otadump::{ExtractOptions, ProgressReporter}; +use otadump::{CancellationToken, ExtractOptions, ProgressReporter, is_cancellation}; -fn main() -> Result<()> { - extract(); +fn main() -> Result<(), ctrlc::Error> { + let cancellation_token = CancellationToken::new(); + let signal_token = cancellation_token.clone(); + ctrlc::set_handler(move || signal_token.cancel())?; + if extract(&cancellation_token) || cancellation_token.is_cancelled() { + std::process::exit(130); + } Ok(()) } @@ -45,14 +49,14 @@ pub struct Args { pub source_dir: Option, } -pub fn extract() { +pub fn extract(cancellation_token: &CancellationToken) -> bool { let args = Args::parse(); let reporter = Box::new(CliProgressReporter::new()); let reporter = reporter.as_ref(); let mut options = ExtractOptions::new(); - options.progress_reporter(reporter); + options.progress_reporter(reporter).cancellation_token(cancellation_token); if let Some(source_dir) = &args.source_dir { options.source_dir(source_dir); } @@ -62,10 +66,15 @@ pub fn extract() { Ok(()) => { let message = format!("Extraction complete: {}", args.output_dir); reporter.progress_bar.println(message); + false } Err(e) => { + if is_cancellation(e.as_ref()) { + return true; + } let message = format!("Error: {e:?}"); reporter.progress_bar.println(message); + false } } } diff --git a/src/python.rs b/src/python.rs index a0e81be..5ee7f16 100644 --- a/src/python.rs +++ b/src/python.rs @@ -1,13 +1,35 @@ use std::path::PathBuf; +use std::time::Duration; use pyo3::create_exception; -use pyo3::exceptions::PyException; +use pyo3::exceptions::{PyException, PyKeyboardInterrupt}; use pyo3::prelude::*; -use crate::ExtractOptions; +use crate::{CancellationToken, ExtractOptions, is_cancellation}; create_exception!(otadump, OtaDumpError, PyException); +#[pyclass(name = "CancellationToken")] +struct PyCancellationToken { + token: CancellationToken, +} + +#[pymethods] +impl PyCancellationToken { + #[new] + fn new() -> Self { + Self { token: CancellationToken::new() } + } + + fn cancel(&self) { + self.token.cancel(); + } + + fn is_cancelled(&self) -> bool { + self.token.is_cancelled() + } +} + /// Extract partitions from an Android OTA payload or OTA ZIP. #[pyfunction] #[allow(clippy::too_many_arguments)] @@ -20,6 +42,7 @@ create_exception!(otadump, OtaDumpError, PyException); partitions = None, verify = true, source_dir = None, + cancellation_token = None, ))] fn extract( py: Python<'_>, @@ -30,9 +53,12 @@ fn extract( partitions: Option>, verify: bool, source_dir: Option, + cancellation_token: Option>, ) -> PyResult<()> { + let cancellation_token = + cancellation_token.map(|token| token.token.clone()).unwrap_or_default(); let mut options = ExtractOptions::new(); - options.overwrite(overwrite).verify(verify); + options.overwrite(overwrite).verify(verify).cancellation_token(&cancellation_token); if let Some(num_threads) = num_threads { options.num_threads(num_threads); @@ -44,16 +70,38 @@ fn extract( options.source_dir(source_dir); } - py.detach(move || { - options - .extract(payload_file, output_dir) - .map_err(|error| OtaDumpError::new_err(error.to_string())) - }) + let extraction = std::thread::spawn(move || match options.extract(payload_file, output_dir) { + Ok(()) => Ok(()), + Err(error) if is_cancellation(error.as_ref()) => Err(None), + Err(error) => Err(Some(error.to_string())), + }); + + let mut signal_error = None; + while !extraction.is_finished() { + if signal_error.is_none() { + if let Err(error) = py.check_signals() { + cancellation_token.cancel(); + signal_error = Some(error); + } + } + py.detach(|| std::thread::sleep(Duration::from_millis(20))); + } + let result = + extraction.join().map_err(|_| OtaDumpError::new_err("Extraction worker panicked"))?; + if let Some(error) = signal_error { + return Err(error); + } + match result { + Ok(()) => Ok(()), + Err(None) => Err(PyKeyboardInterrupt::new_err("Extraction cancelled")), + Err(Some(error)) => Err(OtaDumpError::new_err(error)), + } } #[pymodule] fn otadump(module: &Bound<'_, PyModule>) -> PyResult<()> { module.add("OtaDumpError", module.py().get_type::())?; + module.add_class::()?; module.add_function(wrap_pyfunction!(extract, module)?)?; Ok(()) } diff --git a/tests/delta_extraction.rs b/tests/delta_extraction.rs index 878d5a1..1ce68d1 100644 --- a/tests/delta_extraction.rs +++ b/tests/delta_extraction.rs @@ -1,7 +1,8 @@ use std::fs; use std::path::Path; +use std::sync::atomic::{AtomicBool, Ordering}; -use otadump::ExtractOptions; +use otadump::{CancellationToken, ExtractOptions, ProgressReporter, is_cancellation}; use prost::Message; use ring::digest; use tempfile::TempDir; @@ -468,6 +469,162 @@ fn partition_selection_does_not_require_unselected_delta_sources() { assert!(!output_dir.join("system.img").exists()); } +struct CancellingReporter { + cancellation_token: CancellationToken, + cancelled: AtomicBool, +} + +impl ProgressReporter for CancellingReporter { + fn report_progress(&self, progress: f64) { + if progress > 0.0 && !self.cancelled.swap(true, Ordering::AcqRel) { + self.cancellation_token.cancel(); + } + } +} + +#[test] +fn cancellation_cleans_staging_without_promoting_or_modifying_source() { + let temporary = TempDir::new().unwrap(); + let source_dir = temporary.path().join("source"); + let output_dir = temporary.path().join("output"); + fs::create_dir(&source_dir).unwrap(); + fs::create_dir(&output_dir).unwrap(); + let source = (0..128).map(|byte| byte as u8).collect::>(); + fs::write(source_dir.join("system.img"), &source).unwrap(); + let existing = b"existing destination"; + fs::write(output_dir.join("system.img"), existing).unwrap(); + let operations = (0..32) + .map(|block| InstallOperation { + operation_type: 4, + src_extents: vec![extent(block, 1)], + src_length: Some(4), + dst_extents: vec![extent(block, 1)], + dst_length: Some(4), + ..Default::default() + }) + .collect(); + let manifest = DeltaArchiveManifest { + block_size: Some(4), + minor_version: Some(2), + partitions: vec![PartitionUpdate { + partition_name: "system".into(), + old_partition_info: Some(partition_info(&source)), + new_partition_info: Some(partition_info(&source)), + operations, + }], + }; + let payload = temporary.path().join("payload.bin"); + write_payload(&payload, manifest, &[]); + let cancellation_token = CancellationToken::new(); + let reporter = CancellingReporter { + cancellation_token: cancellation_token.clone(), + cancelled: AtomicBool::new(false), + }; + + let error = ExtractOptions::new() + .source_dir(&source_dir) + .overwrite(true) + .num_threads(4) + .progress_reporter(&reporter) + .cancellation_token(&cancellation_token) + .extract(&payload, &output_dir) + .unwrap_err(); + + assert!(is_cancellation(error.as_ref())); + assert_eq!(fs::read(output_dir.join("system.img")).unwrap(), existing); + assert_eq!(fs::read(source_dir.join("system.img")).unwrap(), source); + assert!( + fs::read_dir(&output_dir).unwrap().all(|entry| !entry + .unwrap() + .file_name() + .to_string_lossy() + .starts_with(".otadump-")) + ); +} + +#[cfg(unix)] +#[test] +fn cli_sigint_exits_130_after_cleaning_staging() { + use std::process::{Command, Stdio}; + use std::time::{Duration, Instant}; + + let temporary = TempDir::new().unwrap(); + let source_dir = temporary.path().join("source"); + let output_dir = temporary.path().join("output"); + fs::create_dir(&source_dir).unwrap(); + let block_size = 256usize; + let block_count = 65_536usize; + let source = vec![0x5a; block_size * block_count]; + let source_path = source_dir.join("system.img"); + fs::write(&source_path, &source).unwrap(); + let operations = (0..block_count) + .map(|block| InstallOperation { + operation_type: 4, + src_extents: vec![extent(block as u64, 1)], + src_length: Some(block_size as u64), + dst_extents: vec![extent(block as u64, 1)], + dst_length: Some(block_size as u64), + ..Default::default() + }) + .collect(); + let manifest = DeltaArchiveManifest { + block_size: Some(block_size as u32), + minor_version: Some(2), + partitions: vec![PartitionUpdate { + partition_name: "system".into(), + old_partition_info: Some(partition_info(&source)), + new_partition_info: Some(partition_info(&source)), + operations, + }], + }; + let payload = temporary.path().join("payload.bin"); + write_payload(&payload, manifest, &[]); + + let mut child = Command::new(env!("CARGO_BIN_EXE_otadump")) + .arg(&payload) + .arg("--output-dir") + .arg(&output_dir) + .arg("--source-dir") + .arg(&source_dir) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn() + .unwrap(); + let deadline = Instant::now() + Duration::from_secs(10); + loop { + let staging_exists = output_dir.exists() + && fs::read_dir(&output_dir) + .unwrap() + .any(|entry| entry.unwrap().file_name().to_string_lossy().starts_with(".otadump-")); + if staging_exists { + break; + } + assert!(child.try_wait().unwrap().is_none(), "CLI finished before SIGINT"); + assert!(Instant::now() < deadline, "staging directory was not created"); + std::thread::yield_now(); + } + let signal_status = + Command::new("kill").arg("-INT").arg(child.id().to_string()).status().unwrap(); + assert!(signal_status.success()); + let exit_deadline = Instant::now() + Duration::from_secs(10); + let status = loop { + if let Some(status) = child.try_wait().unwrap() { + break status; + } + if Instant::now() >= exit_deadline { + child.kill().unwrap(); + child.wait().unwrap(); + panic!("CLI did not finish cleanup after SIGINT"); + } + std::thread::sleep(Duration::from_millis(1)); + }; + + assert_eq!(status.code(), Some(130)); + assert!(!output_dir.join("system.img").exists()); + assert_eq!(sha256(&fs::read(source_path).unwrap()), sha256(&source)); + assert!(fs::read_dir(&output_dir).unwrap().next().is_none()); +} + #[cfg(unix)] #[test] fn source_and_destination_aliases_are_rejected_without_modifying_the_source() { diff --git a/tests/python_smoke.py b/tests/python_smoke.py index d32ba71..4997436 100644 --- a/tests/python_smoke.py +++ b/tests/python_smoke.py @@ -23,6 +23,22 @@ def main() -> None: else: raise AssertionError("invalid payload unexpectedly succeeded") + cancellation_token = otadump.CancellationToken() + cancellation_token.cancel() + cancellation_token.cancel() + cancelled_output = temporary_path / "cancelled-output" + try: + otadump.extract( + payload_file, + cancelled_output, + cancellation_token=cancellation_token, + ) + except KeyboardInterrupt: + pass + else: + raise AssertionError("cancelled extraction unexpectedly succeeded") + assert not cancelled_output.exists() + if __name__ == "__main__": main() From da3d1043a5fc57c0f0967354db1241cef6103753 Mon Sep 17 00:00:00 2001 From: Omkar Chandorkar Date: Fri, 11 Sep 2026 14:57:28 +0530 Subject: [PATCH 03/42] extractor: Apply BSDIFF delta operations Signed-off-by: Omkar Chandorkar --- Cargo.lock | 63 +++++++++++++++- Cargo.toml | 2 + README.md | 3 +- src/lib.rs | 46 ++++++++++++ tests/delta_extraction.rs | 153 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 264 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6ed9355..a550f90 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195" +dependencies = [ + "alloc-no-stdlib", +] + [[package]] name = "anstream" version = "0.6.19" @@ -91,6 +106,37 @@ dependencies = [ "objc2", ] +[[package]] +name = "brotli" +version = "8.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc91aac060a7a1e25823bdccbfb6af1875b88f17c6daac97894eed8207166b3" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "5.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bsdiff-android" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6e86c63a796e87fc125590d0dd6e6933a873642e6f8bbfea8eb31bb527ce56" +dependencies = [ + "brotli", + "bzip2", +] + [[package]] name = "bumpalo" version = "3.18.1" @@ -105,13 +151,24 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "bzip2" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bea8dcd42434048e4f7a304411d9273a411f647446c1234a65ce0554923f4cff" +checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c" dependencies = [ + "bzip2-sys", "libbz2-rs-sys", ] +[[package]] +name = "bzip2-sys" +version = "0.1.13+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" +dependencies = [ + "cc", + "pkg-config", +] + [[package]] name = "cc" version = "1.2.27" @@ -575,6 +632,8 @@ name = "otadump" version = "0.1.3" dependencies = [ "anyhow", + "brotli", + "bsdiff-android", "bzip2", "clap", "color-print", diff --git a/Cargo.toml b/Cargo.toml index e5e946f..1920a7b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,8 @@ protoc-bin-vendored = "3.2.0" [dependencies] anyhow = "1.0.69" +brotli = "8.0.2" +bsdiff-android = "0.0.2" bzip2 = { version = "0.6.0" } clap = { version = "4.2.1", features = ["derive"] } color-print = "0.3.4" diff --git a/README.md b/README.md index 3bdc4d2..f4bf7b6 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,8 @@ otadump ota.zip # Run on payload.bin file. otadump payload.bin -# Apply a SOURCE_COPY delta payload with matching base partition images. +# Apply a SOURCE_COPY, SOURCE_BSDIFF, or BROTLI_BSDIFF delta payload with +# matching base partition images. otadump delta.zip --output-dir output --source-dir source-images ``` diff --git a/src/lib.rs b/src/lib.rs index a8b8c75..7c1446c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,6 +19,7 @@ use std::sync::{Arc, OnceLock}; use std::{error, result, slice, thread}; use anyhow::{Context as _, Error, Result, bail, ensure}; +use bsdiff_android::patch_bsdf2; use bzip2::read::BzDecoder; use chromeos_update_engine::install_operation::Type; use chromeos_update_engine::{DeltaArchiveManifest, InstallOperation, PartitionUpdate}; @@ -853,6 +854,14 @@ impl Task<'_> { self.write_exact(&source, &mut dst_extents) .context("Error in SOURCE_COPY operation") } + Type::SourceBsdiff => { + let patch = self.extract_data(op).context("Error extracting SOURCE_BSDIFF data")?; + self.run_op_bsdiff(op, patch, "SOURCE_BSDIFF", &mut dst_extents) + } + Type::BrotliBsdiff => { + let patch = self.extract_data(op).context("Error extracting BROTLI_BSDIFF data")?; + self.run_op_bsdiff(op, patch, "BROTLI_BSDIFF", &mut dst_extents) + } Type::Zero | Type::Discard => { for extent in dst_extents { for chunk in extent.chunks_mut(VERIFY_CHUNK_SIZE) { @@ -884,6 +893,30 @@ impl Task<'_> { Ok(data) } + fn run_op_bsdiff( + &self, + op: &InstallOperation, + patch: &[u8], + operation: &str, + dst_extents: &mut [&mut [u8]], + ) -> Result<()> { + let source = self.extract_source_data(op)?; + let expected_output_len = dst_extents.iter().try_fold(0usize, |len, extent| { + len.checked_add(extent.len()).context("Combined destination length overflow") + })?; + validate_bsdiff_output_len(patch, expected_output_len) + .map_err(|error| anyhow::anyhow!("{operation} patch is invalid: {error}"))?; + self.cancellation_token.check()?; + let mut output = Vec::new(); + output + .try_reserve_exact(expected_output_len) + .context("Unable to allocate patched output buffer")?; + patch_bsdf2(&source, patch, &mut output) + .map_err(|error| anyhow::anyhow!("{operation} patch is invalid: {error}"))?; + self.cancellation_token.check()?; + self.write_exact(&output, dst_extents) + } + fn write_exact(&self, data: &[u8], dst_extents: &mut [&mut [u8]]) -> Result<()> { let dst_len = dst_extents.iter().try_fold(0usize, |len, extent| { len.checked_add(extent.len()).context("Combined destination length overflow") @@ -1002,6 +1035,19 @@ impl Task<'_> { } } +fn validate_bsdiff_output_len(patch: &[u8], expected: usize) -> Result<()> { + ensure!(patch.len() >= 32, "Patch data too short"); + ensure!( + &patch[..8] == b"BSDIFF40" || &patch[..5] == b"BSDF2", + "Invalid BSDIFF/BSDF2 magic header" + ); + let encoded = u64::from_le_bytes(patch[24..32].try_into().unwrap()); + ensure!(encoded & (1 << 63) == 0, "Negative output length in patch header"); + let actual = usize::try_from(encoded).context("Patch output length is too large")?; + ensure!(actual == expected, "Patch output length mismatch: expected {expected}, got {actual}"); + Ok(()) +} + pub trait ProgressReporter: Sync { /// Reports the progress of the extraction process. The progress is provided /// as a value between 0 and 1. diff --git a/tests/delta_extraction.rs b/tests/delta_extraction.rs index 1ce68d1..a0ef81b 100644 --- a/tests/delta_extraction.rs +++ b/tests/delta_extraction.rs @@ -71,6 +71,26 @@ fn sha256(bytes: &[u8]) -> Vec { digest::digest(&digest::SHA256, bytes).as_ref().to_vec() } +// Hand-authored BSDIFF40 and BSDF2 fixtures. The test data is covered by this +// project's MIT license and was not generated by the patch implementation. +const BSDIFF40_PATCH: &[u8] = &[ + 66, 83, 68, 73, 70, 70, 52, 48, 41, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, + 0, 0, 0, 0, 66, 90, 104, 57, 49, 65, 89, 38, 83, 89, 184, 173, 85, 59, 0, 0, 2, 96, 0, 64, 64, + 8, 0, 32, 0, 48, 204, 12, 245, 5, 206, 46, 228, 138, 112, 161, 33, 113, 90, 170, 118, 66, 90, + 104, 57, 49, 65, 89, 38, 83, 89, 168, 46, 248, 254, 0, 0, 2, 192, 0, 192, 0, 2, 0, 32, 0, 48, + 128, 103, 161, 185, 11, 185, 34, 156, 40, 72, 84, 23, 124, 127, 0, 66, 90, 104, 57, 23, 114, + 69, 56, 80, 144, 0, 0, 0, 0, +]; +const BROTLI_BSDF2_PATCH: &[u8] = &[ + 66, 83, 68, 70, 50, 2, 2, 2, 12, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, + 0, 0, 0, 31, 23, 0, 248, 167, 0, 16, 98, 141, 0, 160, 0, 143, 3, 128, 0, 0, 245, 245, 0, 0, 0, + 0, 3, 63, +]; +const BROTLI_INVALID_HEADER_PATCH: &[u8] = &[ + 66, 83, 68, 70, 50, 2, 2, 2, 12, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +]; + fn extent(start_block: u64, num_blocks: u64) -> Extent { Extent { start_block: Some(start_block), num_blocks: Some(num_blocks) } } @@ -132,6 +152,139 @@ fn source_copy_reconstructs_noncontiguous_extents_without_modifying_source() { assert_eq!(fs::read(source_dir.join("system.img")).unwrap(), source); } +#[test] +fn source_bsdiff_reconstructs_bsdiff40_without_modifying_source() { + let temporary = TempDir::new().unwrap(); + let source_dir = temporary.path().join("source"); + let output_dir = temporary.path().join("output"); + fs::create_dir(&source_dir).unwrap(); + let source = b"abcdefgh"; + let expected = b"abXYefgh"; + fs::write(source_dir.join("system.img"), source).unwrap(); + let manifest = DeltaArchiveManifest { + block_size: Some(1), + minor_version: Some(2), + partitions: vec![PartitionUpdate { + partition_name: "system".into(), + old_partition_info: Some(partition_info(source)), + new_partition_info: Some(partition_info(expected)), + operations: vec![InstallOperation { + operation_type: 5, + data_offset: Some(0), + data_length: Some(BSDIFF40_PATCH.len() as u64), + src_extents: vec![extent(0, source.len() as u64)], + src_length: Some(source.len() as u64), + dst_extents: vec![extent(0, expected.len() as u64)], + dst_length: Some(expected.len() as u64), + data_sha256_hash: Some(sha256(BSDIFF40_PATCH)), + src_sha256_hash: Some(sha256(source)), + }], + }], + }; + let payload = temporary.path().join("payload.bin"); + write_payload(&payload, manifest, BSDIFF40_PATCH); + + ExtractOptions::new() + .source_dir(&source_dir) + .num_threads(2) + .extract(&payload, &output_dir) + .unwrap(); + + assert_eq!(fs::read(output_dir.join("system.img")).unwrap(), expected); + assert_eq!(fs::read(source_dir.join("system.img")).unwrap(), source); +} + +#[test] +fn brotli_bsdiff_reconstructs_bsdf2_without_modifying_source() { + let temporary = TempDir::new().unwrap(); + let source_dir = temporary.path().join("source"); + let output_dir = temporary.path().join("output"); + fs::create_dir(&source_dir).unwrap(); + let source = b"abcdefgh"; + let expected = b"abXYefgh"; + fs::write(source_dir.join("system.img"), source).unwrap(); + let manifest = DeltaArchiveManifest { + block_size: Some(1), + minor_version: Some(4), + partitions: vec![PartitionUpdate { + partition_name: "system".into(), + old_partition_info: Some(partition_info(source)), + new_partition_info: Some(partition_info(expected)), + operations: vec![InstallOperation { + operation_type: 10, + data_offset: Some(0), + data_length: Some(BROTLI_BSDF2_PATCH.len() as u64), + src_extents: vec![extent(0, source.len() as u64)], + src_length: Some(source.len() as u64), + dst_extents: vec![extent(0, expected.len() as u64)], + dst_length: Some(expected.len() as u64), + data_sha256_hash: Some(sha256(BROTLI_BSDF2_PATCH)), + src_sha256_hash: Some(sha256(source)), + }], + }], + }; + let payload = temporary.path().join("payload.bin"); + write_payload(&payload, manifest, BROTLI_BSDF2_PATCH); + + ExtractOptions::new() + .source_dir(&source_dir) + .num_threads(2) + .extract(&payload, &output_dir) + .unwrap(); + + assert_eq!(fs::read(output_dir.join("system.img")).unwrap(), expected); + assert_eq!(fs::read(source_dir.join("system.img")).unwrap(), source); +} + +#[test] +fn malformed_bsdiff_headers_fail_cleanly_and_remove_staging() { + for (operation_type, patch, expected_error) in [ + (5, &[0; 32][..], "SOURCE_BSDIFF patch is invalid"), + (10, BROTLI_INVALID_HEADER_PATCH, "BROTLI_BSDIFF patch is invalid"), + ] { + let temporary = TempDir::new().unwrap(); + let source_dir = temporary.path().join("source"); + let output_dir = temporary.path().join("output"); + fs::create_dir(&source_dir).unwrap(); + let source = b"abcdefgh"; + fs::write(source_dir.join("system.img"), source).unwrap(); + let manifest = DeltaArchiveManifest { + block_size: Some(1), + minor_version: Some(4), + partitions: vec![PartitionUpdate { + partition_name: "system".into(), + old_partition_info: Some(partition_info(source)), + new_partition_info: Some(partition_info(b"abXYefgh")), + operations: vec![InstallOperation { + operation_type, + data_offset: Some(0), + data_length: Some(patch.len() as u64), + src_extents: vec![extent(0, source.len() as u64)], + src_length: Some(source.len() as u64), + dst_extents: vec![extent(0, source.len() as u64)], + dst_length: Some(source.len() as u64), + data_sha256_hash: Some(sha256(patch)), + src_sha256_hash: Some(sha256(source)), + }], + }], + }; + let payload = temporary.path().join("payload.bin"); + write_payload(&payload, manifest, patch); + + let error = ExtractOptions::new() + .source_dir(&source_dir) + .num_threads(2) + .extract(&payload, &output_dir) + .unwrap_err() + .to_string(); + + assert!(error.contains(expected_error), "unexpected error: {error}"); + assert_eq!(fs::read(source_dir.join("system.img")).unwrap(), source); + assert!(!output_dir.join("system.img").exists()); + assert!(fs::read_dir(&output_dir).unwrap().next().is_none()); + } +} + #[test] fn source_validation_rejects_wrong_base_before_creating_output() { let temporary = TempDir::new().unwrap(); From a7d635835917c5b9d102a5b500e66e3816fb1671 Mon Sep 17 00:00:00 2001 From: Omkar Chandorkar Date: Fri, 11 Sep 2026 15:11:39 +0530 Subject: [PATCH 04/42] cli: Return failure for extraction errors Signed-off-by: Omkar Chandorkar --- src/main.rs | 22 ++++++++++++---------- tests/delta_extraction.rs | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/src/main.rs b/src/main.rs index 6a72c93..70e3e6d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,18 @@ +use std::process::ExitCode; + use clap::Parser; use indicatif::{ProgressBar, ProgressStyle}; use otadump::{CancellationToken, ExtractOptions, ProgressReporter, is_cancellation}; -fn main() -> Result<(), ctrlc::Error> { +fn main() -> Result { let cancellation_token = CancellationToken::new(); let signal_token = cancellation_token.clone(); ctrlc::set_handler(move || signal_token.cancel())?; - if extract(&cancellation_token) || cancellation_token.is_cancelled() { - std::process::exit(130); + let exit_code = extract(&cancellation_token); + if cancellation_token.is_cancelled() { + return Ok(ExitCode::from(130)); } - Ok(()) + Ok(exit_code) } const HELP_TEMPLATE: &str = color_print::cstr!( @@ -49,7 +52,7 @@ pub struct Args { pub source_dir: Option, } -pub fn extract(cancellation_token: &CancellationToken) -> bool { +pub fn extract(cancellation_token: &CancellationToken) -> ExitCode { let args = Args::parse(); let reporter = Box::new(CliProgressReporter::new()); @@ -66,15 +69,14 @@ pub fn extract(cancellation_token: &CancellationToken) -> bool { Ok(()) => { let message = format!("Extraction complete: {}", args.output_dir); reporter.progress_bar.println(message); - false + ExitCode::SUCCESS } Err(e) => { if is_cancellation(e.as_ref()) { - return true; + return ExitCode::from(130); } - let message = format!("Error: {e:?}"); - reporter.progress_bar.println(message); - false + eprintln!("Error: {e:?}"); + ExitCode::FAILURE } } } diff --git a/tests/delta_extraction.rs b/tests/delta_extraction.rs index a0ef81b..13541aa 100644 --- a/tests/delta_extraction.rs +++ b/tests/delta_extraction.rs @@ -695,6 +695,45 @@ fn cancellation_cleans_staging_without_promoting_or_modifying_source() { ); } +#[test] +fn cli_extraction_error_exits_nonzero_and_reports_diagnostic() { + use std::process::Command; + + let temporary = TempDir::new().unwrap(); + let payload = temporary.path().join("payload.bin"); + let output_dir = temporary.path().join("output"); + let manifest = DeltaArchiveManifest { + block_size: Some(4), + minor_version: Some(2), + partitions: vec![PartitionUpdate { + partition_name: "boot".into(), + old_partition_info: Some(partition_info(b"base")), + new_partition_info: Some(partition_info(b"base")), + operations: vec![InstallOperation { + operation_type: 4, + src_extents: vec![extent(0, 1)], + dst_extents: vec![extent(0, 1)], + ..Default::default() + }], + }], + }; + write_payload(&payload, manifest, &[]); + + let output = Command::new(env!("CARGO_BIN_EXE_otadump")) + .arg(&payload) + .arg("--output-dir") + .arg(&output_dir) + .output() + .unwrap(); + let stderr = String::from_utf8_lossy(&output.stderr); + + assert_eq!(output.status.code(), Some(1), "CLI did not exit with code 1; stderr: {stderr}"); + assert!( + stderr.contains("require a source directory"), + "missing extraction diagnostic: {stderr}" + ); +} + #[cfg(unix)] #[test] fn cli_sigint_exits_130_after_cleaning_staging() { From 2c901600b7c94a8e55e43eb5000dc26343d4923c Mon Sep 17 00:00:00 2001 From: Omkar Chandorkar Date: Fri, 11 Sep 2026 15:35:26 +0530 Subject: [PATCH 05/42] extractor: Accept padded source images Signed-off-by: Omkar Chandorkar --- src/lib.rs | 25 +++++---- tests/delta_extraction.rs | 115 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+), 12 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7c1446c..fdb40b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -464,8 +464,18 @@ impl<'a> ExtractOptions<'a> { .metadata() .with_context(|| format!("Unable to inspect source partition image: {path:?}"))?; ensure!(metadata.is_file(), "Source partition image is not a file: {path:?}"); - let len = usize::try_from(metadata.len()).context("Source partition is too large")?; - let mmap = if len == 0 { + let backing_len = + usize::try_from(metadata.len()).context("Source partition is too large")?; + let info = + update.old_partition_info.as_ref().context("Source partition info is missing")?; + let expected_size = info.size.context("Source partition size is missing")?; + let len = usize::try_from(expected_size).context("Source partition is too large")?; + ensure!( + backing_len >= len, + "Source partition size mismatch for {:?}: expected at least {expected_size}, got {backing_len}", + update.partition_name, + ); + let mmap = if backing_len == 0 { None } else { Some( @@ -475,15 +485,6 @@ impl<'a> ExtractOptions<'a> { }; let source = SourcePartition { path, len, mmap }; - let info = - update.old_partition_info.as_ref().context("Source partition info is missing")?; - let expected_size = info.size.context("Source partition size is missing")?; - ensure!( - usize::try_from(expected_size).ok() == Some(source.len), - "Source partition size mismatch for {:?}: expected {expected_size}, got {}", - update.partition_name, - source.len - ); let expected_hash = info.hash.as_deref().context("Source partition hash is missing")?; verify_hash( source.bytes(), @@ -577,7 +578,7 @@ struct SourcePartition { impl SourcePartition { fn bytes(&self) -> &[u8] { - self.mmap.as_deref().unwrap_or_default() + &self.mmap.as_deref().unwrap_or_default()[..self.len] } } diff --git a/tests/delta_extraction.rs b/tests/delta_extraction.rs index 13541aa..12dea42 100644 --- a/tests/delta_extraction.rs +++ b/tests/delta_extraction.rs @@ -321,6 +321,121 @@ fn source_validation_rejects_wrong_base_before_creating_output() { assert!(!output_dir.join("boot.img").exists()); } +#[test] +fn padded_source_uses_declared_prefix_without_modifying_tail() { + let temporary = TempDir::new().unwrap(); + let source_dir = temporary.path().join("source"); + let output_dir = temporary.path().join("output"); + fs::create_dir(&source_dir).unwrap(); + let declared_source = b"base"; + let source = b"basefooter"; + fs::write(source_dir.join("system.img"), source).unwrap(); + let manifest = DeltaArchiveManifest { + block_size: Some(4), + minor_version: Some(2), + partitions: vec![PartitionUpdate { + partition_name: "system".into(), + old_partition_info: Some(partition_info(declared_source)), + new_partition_info: Some(partition_info(declared_source)), + operations: vec![InstallOperation { + operation_type: 4, + src_extents: vec![extent(0, 1)], + src_length: Some(4), + dst_extents: vec![extent(0, 1)], + dst_length: Some(4), + src_sha256_hash: Some(sha256(declared_source)), + ..Default::default() + }], + }], + }; + let payload = temporary.path().join("payload.bin"); + write_payload(&payload, manifest, &[]); + + ExtractOptions::new().source_dir(&source_dir).extract(&payload, &output_dir).unwrap(); + + assert_eq!(fs::read(output_dir.join("system.img")).unwrap(), declared_source); + assert_eq!(fs::read(source_dir.join("system.img")).unwrap(), source); +} + +#[test] +fn source_shorter_than_declared_size_fails_cleanly() { + let temporary = TempDir::new().unwrap(); + let source_dir = temporary.path().join("source"); + let output_dir = temporary.path().join("output"); + fs::create_dir(&source_dir).unwrap(); + let source = b"bas"; + fs::write(source_dir.join("system.img"), source).unwrap(); + let declared_source = b"base"; + let manifest = DeltaArchiveManifest { + block_size: Some(4), + minor_version: Some(2), + partitions: vec![PartitionUpdate { + partition_name: "system".into(), + old_partition_info: Some(partition_info(declared_source)), + new_partition_info: Some(partition_info(declared_source)), + operations: vec![InstallOperation { + operation_type: 4, + src_extents: vec![extent(0, 1)], + dst_extents: vec![extent(0, 1)], + ..Default::default() + }], + }], + }; + let payload = temporary.path().join("payload.bin"); + write_payload(&payload, manifest, &[]); + + let error = ExtractOptions::new() + .source_dir(&source_dir) + .extract(&payload, &output_dir) + .unwrap_err() + .to_string(); + + assert!(error.contains("Source partition size mismatch"), "unexpected error: {error}"); + assert_eq!(fs::read(source_dir.join("system.img")).unwrap(), source); + assert!(!output_dir.join("system.img").exists()); +} + +#[test] +fn source_extent_cannot_enter_padding_beyond_declared_size() { + let temporary = TempDir::new().unwrap(); + let source_dir = temporary.path().join("source"); + let output_dir = temporary.path().join("output"); + fs::create_dir(&source_dir).unwrap(); + let declared_source = b"base"; + let source = b"basefoot"; + fs::write(source_dir.join("system.img"), source).unwrap(); + let manifest = DeltaArchiveManifest { + block_size: Some(4), + minor_version: Some(2), + partitions: vec![PartitionUpdate { + partition_name: "system".into(), + old_partition_info: Some(partition_info(declared_source)), + new_partition_info: Some(partition_info(declared_source)), + operations: vec![InstallOperation { + operation_type: 4, + src_extents: vec![extent(1, 1)], + src_length: Some(4), + dst_extents: vec![extent(0, 1)], + dst_length: Some(4), + src_sha256_hash: Some(sha256(b"foot")), + ..Default::default() + }], + }], + }; + let payload = temporary.path().join("payload.bin"); + write_payload(&payload, manifest, &[]); + + let error = ExtractOptions::new() + .source_dir(&source_dir) + .extract(&payload, &output_dir) + .unwrap_err() + .to_string(); + + assert!(error.contains("Invalid source extents"), "unexpected error: {error}"); + assert_eq!(fs::read(source_dir.join("system.img")).unwrap(), source); + assert!(!output_dir.join("system.img").exists()); +} + #[test] fn delta_operation_without_source_directory_fails_clearly() { let temporary = TempDir::new().unwrap(); From 4fe2369cfd4363284bbbc96029c11af917e03790 Mon Sep 17 00:00:00 2001 From: Omkar Chandorkar Date: Fri, 11 Sep 2026 16:23:41 +0530 Subject: [PATCH 06/42] verity: Generate partition hash trees Signed-off-by: Omkar Chandorkar --- src/lib.rs | 141 ++++++++++++++++-------- src/verity.rs | 187 ++++++++++++++++++++++++++++++++ tests/delta_extraction.rs | 221 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 502 insertions(+), 47 deletions(-) create mode 100644 src/verity.rs diff --git a/src/lib.rs b/src/lib.rs index fdb40b0..481528e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,10 +6,11 @@ mod chromeos_update_engine { mod payload; #[cfg(feature = "python")] mod python; +mod verity; use std::cmp::Reverse; use std::collections::HashSet; -use std::fs::{self, File}; +use std::fs::{self, File, OpenOptions}; use std::io::Read; use std::num::NonZero; use std::ops::Range; @@ -26,7 +27,7 @@ use chromeos_update_engine::{DeltaArchiveManifest, InstallOperation, PartitionUp use liblzma::read::XzDecoder; use memmap2::{Mmap, MmapMut, MmapOptions}; use prost::Message as _; -use rayon::{BroadcastContext, ThreadPoolBuilder}; +use rayon::ThreadPoolBuilder; use ring::digest; use sync_unsafe_cell::SyncUnsafeCell; use tempfile::TempPath; @@ -126,7 +127,7 @@ impl<'a> ExtractOptions<'a> { let output_dir = output_dir.as_ref(); match self.extract_impl(payload_file, output_dir) { Ok(()) => Ok(()), - Err(error) if error.is::() => Err(Box::new(ExtractionCancelled)), + Err(error) if is_cancellation(error.as_ref()) => Err(Box::new(ExtractionCancelled)), Err(error) => Err(error.into()), } } @@ -183,11 +184,12 @@ impl<'a> ExtractOptions<'a> { fs::create_dir_all(output_dir) .with_context(|| format!("Could not create output directory: {output_dir:?}"))?; - let sources = manifest + let validated = manifest .partitions .iter() .map(|update| self.validate_partition(update, &payload, block_size)) .collect::>>()?; + let (sources, verity_configs): (Vec<_>, Vec<_>) = validated.into_iter().unzip(); let mut partition_names = HashSet::with_capacity(manifest.partitions.len()); let mut destinations = Vec::with_capacity(manifest.partitions.len()); @@ -269,8 +271,8 @@ impl<'a> ExtractOptions<'a> { cancellation_token: &self.cancellation_token, error: &error, }; - scope.spawn_broadcast(move |_, ctx| { - if let Err(e) = task.run(ctx) { + scope.spawn_broadcast(move |_, _| { + if let Err(e) = task.run() { _ = task.error.set(e); } }); @@ -282,6 +284,42 @@ impl<'a> ExtractOptions<'a> { return Err(e); } + for ((update, verity_config), staging_path) in + manifest.partitions.iter().zip(verity_configs).zip(&staging_paths) + { + self.cancellation_token.check()?; + let file = OpenOptions::new() + .read(true) + .write(true) + .open(staging_path) + .context("Unable to reopen staged partition image")?; + let mut partition = unsafe { MmapMut::map_mut(&file) } + .context("Failed to mmap staged partition image")?; + if let Some(config) = verity_config.as_ref() { + verity::generate(&mut partition, config, block_size, &self.cancellation_token) + .map_err(|error| { + if is_cancellation(error.as_ref()) { + error + } else { + error.context(format!( + "Failed to generate hash tree for {:?}", + update.partition_name + )) + } + })?; + } + verify_partition( + update, + &partition, + total_ops, + &total_ops_completed, + self.progress_reporter, + &self.cancellation_token, + )?; + self.cancellation_token.check()?; + partition.flush().context("Error while flushing file to disk")?; + } + for (staging_path, destination) in staging_paths.into_iter().zip(destinations) { self.cancellation_token.check()?; let result = if self.overwrite { @@ -349,7 +387,7 @@ impl<'a> ExtractOptions<'a> { update: &PartitionUpdate, payload: &Payload<'_>, block_size: usize, - ) -> Result> { + ) -> Result<(Option, Option)> { validate_partition_name(&update.partition_name)?; self.cancellation_token.check()?; @@ -447,7 +485,12 @@ impl<'a> ExtractOptions<'a> { ); } - Ok(source) + let verity = verity::validate(update, block_size, partition_len, &destination_ranges) + .map_err(|error| { + anyhow::anyhow!("Invalid verity metadata for {:?}: {error}", update.partition_name) + })?; + + Ok((source, verity)) } fn open_source_partition(&self, update: &PartitionUpdate) -> Result { @@ -791,7 +834,7 @@ struct Task<'a> { } impl Task<'_> { - fn run(&self, ctx: BroadcastContext<'_>) -> Result<()> { + fn run(&self) -> Result<()> { // If an error has already occurred, stop processing the partition. while self.error.get().is_none() { self.cancellation_token.check()?; @@ -803,16 +846,6 @@ impl Task<'_> { self.increment_progress(); } None => { - // If this is the last thread to fetch an operation for this - // partition, the partition is fully extracted and can now be - // verified. - if op_idx + 1 == self.update.operations.len() + ctx.num_threads() { - self.cancellation_token.check()?; - self.verify_partition()?; - self.cancellation_token.check()?; - unsafe { (*self.partition.get()).flush() } - .context("Error while flushing file to disk")?; - }; break; } } @@ -1001,38 +1034,52 @@ impl Task<'_> { verify_hash(data, op.data_sha256_hash.as_deref(), "Input", self.cancellation_token) } - fn verify_partition(&self) -> Result<()> { - let exp_hash = self - .update - .new_partition_info - .as_ref() - .and_then(|info| info.hash.as_ref()) - .context("Unable to determine output partition hash")?; + fn increment_progress(&self) { + increment_progress(self.total_ops, self.total_ops_completed, self.progress_reporter); + } +} - let mut digest = digest::Context::new(&digest::SHA256); - for chunk in unsafe { (*self.partition.get()).chunks(VERIFY_CHUNK_SIZE) } { - self.cancellation_token.check()?; - digest.update(chunk); - self.increment_progress(); - } +fn verify_partition( + update: &PartitionUpdate, + partition: &[u8], + total_ops: usize, + total_ops_completed: &AtomicUsize, + progress_reporter: &dyn ProgressReporter, + cancellation_token: &CancellationToken, +) -> Result<()> { + let exp_hash = update + .new_partition_info + .as_ref() + .and_then(|info| info.hash.as_ref()) + .context("Unable to determine output partition hash")?; - self.cancellation_token.check()?; - let got_hash = digest.finish(); - let got_hash = got_hash.as_ref(); - ensure!( - got_hash == exp_hash, - "Output verification failed: hash mismatch: expected {}, got {got_hash:?}", - hex::encode(exp_hash) - ); - Ok(()) + let mut context = digest::Context::new(&digest::SHA256); + for chunk in partition.chunks(VERIFY_CHUNK_SIZE) { + cancellation_token.check()?; + context.update(chunk); + increment_progress(total_ops, total_ops_completed, progress_reporter); } - fn increment_progress(&self) { - let total_ops_completed = self.total_ops_completed.fetch_add(1, Ordering::Relaxed) + 1; - if total_ops_completed % 16 == 0 { - let progress = total_ops_completed as f64 / self.total_ops as f64; - self.progress_reporter.report_progress(progress); - } + cancellation_token.check()?; + let got_hash = context.finish(); + ensure!( + got_hash.as_ref() == exp_hash, + "Output verification failed: hash mismatch: expected {}, got {}", + hex::encode(exp_hash), + hex::encode(got_hash.as_ref()) + ); + Ok(()) +} + +fn increment_progress( + total_ops: usize, + total_ops_completed: &AtomicUsize, + progress_reporter: &dyn ProgressReporter, +) { + let total_ops_completed = total_ops_completed.fetch_add(1, Ordering::Relaxed) + 1; + if total_ops_completed % 16 == 0 { + let progress = total_ops_completed as f64 / total_ops as f64; + progress_reporter.report_progress(progress); } } diff --git a/src/verity.rs b/src/verity.rs new file mode 100644 index 0000000..e917adf --- /dev/null +++ b/src/verity.rs @@ -0,0 +1,187 @@ +use std::ops::Range; + +use anyhow::{Context as _, Result, bail, ensure}; +use ring::digest; + +use crate::CancellationToken; +use crate::chromeos_update_engine::{Extent, PartitionUpdate}; + +const SHA256_SIZE: usize = 32; + +pub(crate) struct VerityConfig { + data: Range, + tree: Range, + salt: Vec, +} + +pub(crate) fn validate( + update: &PartitionUpdate, + block_size: usize, + partition_len: usize, + operation_destinations: &[(Range, usize)], +) -> Result> { + if extent_num_blocks(update.fec_extent.as_ref(), "FEC")?.unwrap_or(0) > 0 { + bail!("FEC generation is not supported for partition {:?}", update.partition_name); + } + + let data_blocks = + extent_num_blocks(update.hash_tree_data_extent.as_ref(), "Hash tree data")?.unwrap_or(0); + let tree_blocks = + extent_num_blocks(update.hash_tree_extent.as_ref(), "Hash tree")?.unwrap_or(0); + ensure!( + (data_blocks == 0) == (tree_blocks == 0), + "Hash tree data and storage extents must both be nonempty" + ); + if tree_blocks == 0 { + return Ok(None); + } + + let algorithm = update.hash_tree_algorithm.as_deref().unwrap_or_default(); + let is_sha256 = + algorithm.eq_ignore_ascii_case("sha256") || algorithm.eq_ignore_ascii_case("sha-256"); + ensure!(is_sha256, "Unsupported verity hash algorithm: {algorithm:?}"); + ensure!( + block_size > SHA256_SIZE * 2 && block_size % SHA256_SIZE == 0, + "Block size {block_size} is not supported for SHA-256 verity trees" + ); + + let data_extent = + update.hash_tree_data_extent.as_ref().context("Hash tree data extent is missing")?; + let tree_extent = + update.hash_tree_extent.as_ref().context("Hash tree storage extent is missing")?; + let data = extent_range(data_extent, block_size, partition_len, "Hash tree data")?; + let tree = extent_range(tree_extent, block_size, partition_len, "Hash tree")?; + ensure!(!ranges_overlap(&data, &tree), "Hash tree data and storage extents overlap"); + ensure!( + operation_destinations.iter().all(|(destination, _)| !ranges_overlap(&tree, destination)), + "Hash tree extent overlaps destination extents" + ); + + let hashes_per_block = block_size / SHA256_SIZE; + let mut level_blocks = div_ceil(data_blocks, hashes_per_block)?; + let mut required_blocks = 0usize; + loop { + required_blocks = + required_blocks.checked_add(level_blocks).context("Hash tree size overflow")?; + if level_blocks == 1 { + break; + } + level_blocks = div_ceil(level_blocks, hashes_per_block)?; + } + let required_size = + required_blocks.checked_mul(block_size).context("Hash tree size overflow")?; + ensure!( + tree.len() == required_size, + "Hash tree size mismatch: expected {required_size} bytes, got {}", + tree.len() + ); + + Ok(Some(VerityConfig { data, tree, salt: update.hash_tree_salt.clone().unwrap_or_default() })) +} + +pub(crate) fn generate( + partition: &mut [u8], + config: &VerityConfig, + block_size: usize, + cancellation_token: &CancellationToken, +) -> Result<()> { + cancellation_token.check()?; + let hashes_per_block = block_size / SHA256_SIZE; + let data_blocks = config.data.len() / block_size; + let base_blocks = div_ceil(data_blocks, hashes_per_block)?; + let mut level = zeroed_level(base_blocks, block_size)?; + for (block, hash_slot) in partition[config.data.clone()] + .chunks_exact(block_size) + .zip(level.chunks_exact_mut(SHA256_SIZE)) + { + cancellation_token.check()?; + hash_slot.copy_from_slice(salted_hash(&config.salt, block).as_ref()); + } + + let mut levels = vec![level]; + while levels.last().expect("base level exists").len() > block_size { + let current = levels.last().expect("base level exists"); + let next_blocks = div_ceil(current.len() / block_size, hashes_per_block)?; + let mut next = zeroed_level(next_blocks, block_size)?; + for (block, hash_slot) in + current.chunks_exact(block_size).zip(next.chunks_exact_mut(SHA256_SIZE)) + { + cancellation_token.check()?; + hash_slot.copy_from_slice(salted_hash(&config.salt, block).as_ref()); + } + levels.push(next); + } + + let tree = &mut partition[config.tree.clone()]; + let mut offset = 0usize; + for level in levels.iter().rev() { + for block in level.chunks_exact(block_size) { + cancellation_token.check()?; + let end = offset.checked_add(block_size).context("Hash tree write overflow")?; + tree[offset..end].copy_from_slice(block); + offset = end; + } + } + ensure!(offset == tree.len(), "Generated hash tree size mismatch"); + cancellation_token.check() +} + +fn extent_num_blocks(extent: Option<&Extent>, label: &str) -> Result> { + extent + .map(|extent| { + usize::try_from( + extent + .num_blocks + .with_context(|| format!("{label} extent block count is missing"))?, + ) + .with_context(|| format!("{label} extent block count is too large")) + }) + .transpose() +} + +fn extent_range( + extent: &Extent, + block_size: usize, + partition_len: usize, + label: &str, +) -> Result> { + let start_block = usize::try_from( + extent.start_block.with_context(|| format!("{label} extent start block is missing"))?, + ) + .with_context(|| format!("{label} extent start block is too large"))?; + let num_blocks = usize::try_from( + extent.num_blocks.with_context(|| format!("{label} extent block count is missing"))?, + ) + .with_context(|| format!("{label} extent block count is too large"))?; + let start = start_block.checked_mul(block_size).context("Verity extent offset overflow")?; + let len = num_blocks.checked_mul(block_size).context("Verity extent length overflow")?; + let end = start.checked_add(len).context("Verity extent end overflow")?; + ensure!(end <= partition_len, "{label} extent exceeds partition size"); + Ok(start..end) +} + +fn div_ceil(value: usize, divisor: usize) -> Result { + value + .checked_add(divisor - 1) + .context("Hash tree block count overflow") + .map(|rounded| rounded / divisor) +} + +fn zeroed_level(blocks: usize, block_size: usize) -> Result> { + let len = blocks.checked_mul(block_size).context("Hash tree level size overflow")?; + let mut level = Vec::new(); + level.try_reserve_exact(len).context("Unable to allocate hash tree")?; + level.resize(len, 0); + Ok(level) +} + +fn salted_hash(salt: &[u8], block: &[u8]) -> digest::Digest { + let mut context = digest::Context::new(&digest::SHA256); + context.update(salt); + context.update(block); + context.finish() +} + +fn ranges_overlap(left: &Range, right: &Range) -> bool { + left.start < right.end && right.start < left.end +} diff --git a/tests/delta_extraction.rs b/tests/delta_extraction.rs index 12dea42..5eebaac 100644 --- a/tests/delta_extraction.rs +++ b/tests/delta_extraction.rs @@ -55,6 +55,20 @@ struct PartitionUpdate { new_partition_info: Option, #[prost(message, repeated, tag = "8")] operations: Vec, + #[prost(message, optional, tag = "10")] + hash_tree_data_extent: Option, + #[prost(message, optional, tag = "11")] + hash_tree_extent: Option, + #[prost(string, optional, tag = "12")] + hash_tree_algorithm: Option, + #[prost(bytes = "vec", optional, tag = "13")] + hash_tree_salt: Option>, + #[prost(message, optional, tag = "14")] + fec_data_extent: Option, + #[prost(message, optional, tag = "15")] + fec_extent: Option, + #[prost(uint32, optional, tag = "16")] + fec_roots: Option, } #[derive(Clone, PartialEq, Message)] @@ -137,6 +151,7 @@ fn source_copy_reconstructs_noncontiguous_extents_without_modifying_source() { old_partition_info: Some(partition_info(source)), new_partition_info: Some(partition_info(expected)), operations: vec![operation], + ..Default::default() }], }; let payload = temporary.path().join("payload.bin"); @@ -179,6 +194,7 @@ fn source_bsdiff_reconstructs_bsdiff40_without_modifying_source() { data_sha256_hash: Some(sha256(BSDIFF40_PATCH)), src_sha256_hash: Some(sha256(source)), }], + ..Default::default() }], }; let payload = temporary.path().join("payload.bin"); @@ -221,6 +237,7 @@ fn brotli_bsdiff_reconstructs_bsdf2_without_modifying_source() { data_sha256_hash: Some(sha256(BROTLI_BSDF2_PATCH)), src_sha256_hash: Some(sha256(source)), }], + ..Default::default() }], }; let payload = temporary.path().join("payload.bin"); @@ -266,6 +283,7 @@ fn malformed_bsdiff_headers_fail_cleanly_and_remove_staging() { data_sha256_hash: Some(sha256(patch)), src_sha256_hash: Some(sha256(source)), }], + ..Default::default() }], }; let payload = temporary.path().join("payload.bin"); @@ -306,6 +324,7 @@ fn source_validation_rejects_wrong_base_before_creating_output() { old_partition_info: Some(partition_info(b"good")), new_partition_info: Some(partition_info(b"good")), operations: vec![operation], + ..Default::default() }], }; let payload = temporary.path().join("payload.bin"); @@ -346,6 +365,7 @@ fn padded_source_uses_declared_prefix_without_modifying_tail() { src_sha256_hash: Some(sha256(declared_source)), ..Default::default() }], + ..Default::default() }], }; let payload = temporary.path().join("payload.bin"); @@ -379,6 +399,7 @@ fn source_shorter_than_declared_size_fails_cleanly() { dst_extents: vec![extent(0, 1)], ..Default::default() }], + ..Default::default() }], }; let payload = temporary.path().join("payload.bin"); @@ -420,6 +441,7 @@ fn source_extent_cannot_enter_padding_beyond_declared_size() { src_sha256_hash: Some(sha256(b"foot")), ..Default::default() }], + ..Default::default() }], }; let payload = temporary.path().join("payload.bin"); @@ -453,6 +475,7 @@ fn delta_operation_without_source_directory_fails_clearly() { dst_extents: vec![extent(0, 1)], ..Default::default() }], + ..Default::default() }], }; let payload = temporary.path().join("payload.bin"); @@ -510,6 +533,7 @@ fn malformed_partition_layouts_fail_without_escaping_or_leaving_partial_images() old_partition_info: None, new_partition_info: Some(partition_info(target)), operations, + ..Default::default() }], }; let payload = temporary.path().join("payload.bin"); @@ -546,6 +570,7 @@ fn full_replace_payload_remains_compatible() { old_partition_info: None, new_partition_info: Some(partition_info(&padded)), operations: vec![operation], + ..Default::default() }], }; let payload = temporary.path().join("payload.bin"); @@ -556,6 +581,191 @@ fn full_replace_payload_remains_compatible() { assert_eq!(fs::read(output_dir.join("vendor.img")).unwrap(), padded); } +#[test] +fn omitted_verity_tree_is_generated_in_aosp_top_down_order() { + const LEAF_HASHES: [[u8; 32]; 5] = [ + [ + 0x2b, 0xbf, 0x03, 0x32, 0xe3, 0x58, 0xa6, 0xa5, 0xdd, 0xc7, 0x3e, 0xa7, 0x1e, 0x75, + 0xdc, 0x83, 0xf5, 0x8f, 0xec, 0xfc, 0x25, 0x4a, 0x4e, 0x7d, 0x41, 0x33, 0x86, 0x95, + 0x8b, 0xd9, 0xe0, 0x00, + ], + [ + 0x24, 0x59, 0x0e, 0xd6, 0xcb, 0x84, 0x1e, 0xa2, 0xe5, 0xd8, 0x39, 0xe7, 0xc6, 0x3d, + 0xb6, 0x9b, 0x75, 0x50, 0xb4, 0x88, 0x60, 0x7a, 0xf2, 0x52, 0x75, 0x53, 0x0a, 0x5e, + 0x62, 0xce, 0x21, 0x7c, + ], + [ + 0xf1, 0x06, 0x4b, 0x13, 0x33, 0x00, 0xf3, 0x86, 0x53, 0x23, 0x3e, 0x15, 0x04, 0x99, + 0x74, 0x81, 0xa0, 0x0b, 0xa8, 0xa1, 0x17, 0xd2, 0x0e, 0xd6, 0xae, 0x58, 0xc6, 0x75, + 0x8e, 0x69, 0x39, 0xcc, + ], + [ + 0xda, 0x97, 0xb2, 0x05, 0xf9, 0xe7, 0xa3, 0xad, 0x12, 0x0d, 0xf3, 0xb2, 0x03, 0x14, + 0xe2, 0x07, 0x1b, 0xb0, 0xfb, 0x0f, 0x87, 0x0f, 0x95, 0x21, 0xe0, 0xb2, 0xf9, 0x89, + 0x1e, 0xe6, 0x9b, 0xe8, + ], + [ + 0xa7, 0xd9, 0xa0, 0xf7, 0xbf, 0xe5, 0xfa, 0x24, 0x7c, 0x32, 0x31, 0x87, 0x01, 0x9d, + 0x01, 0x25, 0xf6, 0x93, 0xf0, 0xcd, 0xf4, 0x70, 0x93, 0xa2, 0xf1, 0x39, 0x2b, 0x3f, + 0x57, 0xb9, 0xfb, 0x0b, + ], + ]; + const UPPER_HASHES: [[u8; 32]; 2] = [ + [ + 0x73, 0x55, 0xdf, 0xc2, 0xeb, 0x76, 0xd2, 0xb4, 0x0d, 0x27, 0xe3, 0x8c, 0x41, 0x40, + 0x35, 0xa2, 0x7c, 0x5a, 0x8c, 0x95, 0x90, 0x2d, 0xdf, 0x77, 0xbb, 0x87, 0x69, 0x46, + 0x96, 0xd6, 0x1b, 0x91, + ], + [ + 0xfe, 0xb1, 0x40, 0xb5, 0x15, 0x40, 0xbf, 0x36, 0x30, 0x8d, 0xce, 0x5c, 0x31, 0x08, + 0x45, 0xe4, 0x12, 0x51, 0x2e, 0xc6, 0x53, 0xf9, 0x2b, 0x4e, 0x31, 0x09, 0x30, 0x7f, + 0xab, 0x5e, 0x16, 0xe1, + ], + ]; + + let temporary = TempDir::new().unwrap(); + let output_dir = temporary.path().join("output"); + let block_size = 128usize; + let mut data = Vec::with_capacity(5 * block_size); + for byte in [0x11, 0x22, 0x33, 0x44, 0x55] { + data.extend(std::iter::repeat_n(byte, block_size)); + } + let mut expected_tree = Vec::with_capacity(3 * block_size); + expected_tree.extend(UPPER_HASHES.into_iter().flatten()); + expected_tree.resize(block_size, 0); + expected_tree.extend(LEAF_HASHES[..4].iter().flatten()); + expected_tree.extend(LEAF_HASHES[4]); + expected_tree.resize(3 * block_size, 0); + let mut expected = data.clone(); + expected.extend_from_slice(&expected_tree); + let manifest = DeltaArchiveManifest { + block_size: Some(block_size as u32), + minor_version: Some(6), + partitions: vec![PartitionUpdate { + partition_name: "vendor".into(), + old_partition_info: None, + new_partition_info: Some(partition_info(&expected)), + operations: vec![InstallOperation { + operation_type: 0, + data_offset: Some(0), + data_length: Some(data.len() as u64), + dst_extents: vec![extent(0, 5)], + dst_length: Some(data.len() as u64), + data_sha256_hash: Some(sha256(&data)), + ..Default::default() + }], + hash_tree_data_extent: Some(extent(0, 5)), + hash_tree_extent: Some(extent(5, 3)), + hash_tree_algorithm: Some("sha256".into()), + hash_tree_salt: Some(b"verity-test-salt".to_vec()), + ..Default::default() + }], + }; + let payload = temporary.path().join("payload.bin"); + write_payload(&payload, manifest, &data); + + ExtractOptions::new().num_threads(4).extract(&payload, &output_dir).unwrap(); + + let actual = fs::read(output_dir.join("vendor.img")).unwrap(); + assert_eq!(&actual[5 * block_size..], expected_tree); + assert_eq!(actual, expected); +} + +#[test] +fn malformed_verity_layouts_fail_without_leaving_staging_files() { + for (tree_extent, operations, expected_error) in [ + ( + extent(5, 2), + vec![InstallOperation { + operation_type: 0, + data_offset: Some(0), + data_length: Some(5 * 128), + dst_extents: vec![extent(0, 5)], + ..Default::default() + }], + "Hash tree size mismatch", + ), + ( + extent(5, 3), + vec![ + InstallOperation { + operation_type: 0, + data_offset: Some(0), + data_length: Some(5 * 128), + dst_extents: vec![extent(0, 5)], + ..Default::default() + }, + InstallOperation { + operation_type: 6, + dst_extents: vec![extent(5, 1)], + ..Default::default() + }, + ], + "Hash tree extent overlaps destination extents", + ), + ] { + let temporary = TempDir::new().unwrap(); + let output_dir = temporary.path().join("output"); + let data = vec![0x5a; 5 * 128]; + let manifest = DeltaArchiveManifest { + block_size: Some(128), + minor_version: Some(6), + partitions: vec![PartitionUpdate { + partition_name: "vendor".into(), + new_partition_info: Some(partition_info(&vec![0; 8 * 128])), + operations, + hash_tree_data_extent: Some(extent(0, 5)), + hash_tree_extent: Some(tree_extent), + hash_tree_algorithm: Some("sha256".into()), + hash_tree_salt: Some(vec![0x23; 32]), + ..Default::default() + }], + }; + let payload = temporary.path().join("payload.bin"); + write_payload(&payload, manifest, &data); + + let error = ExtractOptions::new().extract(&payload, &output_dir).unwrap_err().to_string(); + + assert!(error.contains(expected_error), "unexpected error: {error}"); + assert!(!output_dir.join("vendor.img").exists()); + assert!(fs::read_dir(&output_dir).unwrap().next().is_none()); + } +} + +#[test] +fn fec_metadata_fails_explicitly_without_publishing_an_image() { + let temporary = TempDir::new().unwrap(); + let output_dir = temporary.path().join("output"); + let data = vec![0x5a; 128]; + let manifest = DeltaArchiveManifest { + block_size: Some(128), + minor_version: Some(6), + partitions: vec![PartitionUpdate { + partition_name: "vendor".into(), + new_partition_info: Some(partition_info(&vec![0; 256])), + operations: vec![InstallOperation { + operation_type: 0, + data_offset: Some(0), + data_length: Some(128), + dst_extents: vec![extent(0, 1)], + ..Default::default() + }], + fec_data_extent: Some(extent(0, 1)), + fec_extent: Some(extent(1, 1)), + fec_roots: Some(2), + ..Default::default() + }], + }; + let payload = temporary.path().join("payload.bin"); + write_payload(&payload, manifest, &data); + + let error = ExtractOptions::new().extract(&payload, &output_dir).unwrap_err().to_string(); + + assert!(error.contains("FEC generation is not supported"), "unexpected error: {error}"); + assert!(!output_dir.join("vendor.img").exists()); + assert!(fs::read_dir(&output_dir).unwrap().next().is_none()); +} + #[test] fn verified_partitions_are_promoted_after_all_succeed() { let temporary = TempDir::new().unwrap(); @@ -577,6 +787,7 @@ fn verified_partitions_are_promoted_after_all_succeed() { dst_extents: vec![extent(0, 1)], ..Default::default() }], + ..Default::default() }, PartitionUpdate { partition_name: "vendor".into(), @@ -589,6 +800,7 @@ fn verified_partitions_are_promoted_after_all_succeed() { dst_extents: vec![extent(0, 1)], ..Default::default() }], + ..Default::default() }, ], }; @@ -625,6 +837,7 @@ fn no_clobber_rejects_existing_destination_without_modifying_source() { dst_extents: vec![extent(0, 1)], ..Default::default() }], + ..Default::default() }], }; let payload = temporary.path().join("payload.bin"); @@ -664,6 +877,7 @@ fn verification_failure_preserves_existing_destinations_with_overwrite() { dst_extents: vec![extent(0, 1)], ..Default::default() }], + ..Default::default() }, PartitionUpdate { partition_name: "vendor".into(), @@ -676,6 +890,7 @@ fn verification_failure_preserves_existing_destinations_with_overwrite() { dst_extents: vec![extent(0, 1)], ..Default::default() }], + ..Default::default() }, ], }; @@ -714,6 +929,7 @@ fn partition_selection_does_not_require_unselected_delta_sources() { dst_extents: vec![extent(0, 1)], ..Default::default() }], + ..Default::default() }, PartitionUpdate { partition_name: "system".into(), @@ -725,6 +941,7 @@ fn partition_selection_does_not_require_unselected_delta_sources() { dst_extents: vec![extent(0, 1)], ..Default::default() }], + ..Default::default() }, ], }; @@ -779,6 +996,7 @@ fn cancellation_cleans_staging_without_promoting_or_modifying_source() { old_partition_info: Some(partition_info(&source)), new_partition_info: Some(partition_info(&source)), operations, + ..Default::default() }], }; let payload = temporary.path().join("payload.bin"); @@ -830,6 +1048,7 @@ fn cli_extraction_error_exits_nonzero_and_reports_diagnostic() { dst_extents: vec![extent(0, 1)], ..Default::default() }], + ..Default::default() }], }; write_payload(&payload, manifest, &[]); @@ -882,6 +1101,7 @@ fn cli_sigint_exits_130_after_cleaning_staging() { old_partition_info: Some(partition_info(&source)), new_partition_info: Some(partition_info(&source)), operations, + ..Default::default() }], }; let payload = temporary.path().join("payload.bin"); @@ -966,6 +1186,7 @@ fn source_and_destination_aliases_are_rejected_without_modifying_the_source() { dst_extents: vec![extent(0, 1)], ..Default::default() }], + ..Default::default() }], }; let payload = temporary.path().join("payload.bin"); From 381000ab83e585d338f5107fbd639efee648ab1c Mon Sep 17 00:00:00 2001 From: Omkar Chandorkar Date: Fri, 11 Sep 2026 18:32:10 +0530 Subject: [PATCH 07/42] zucchini: Vendor audited native boundary Compile the pinned Android 17 Zucchini and libchrome source closure in process. Add a safe Rust buffer/status wrapper and licensed golden fixtures for every Android executable type. Signed-off-by: Omkar Chandorkar --- .gitignore | 2 + Cargo.lock | 1 + Cargo.toml | 1 + build.rs | 122 ++ native/zucchini/README.md | 89 + native/zucchini/SOURCE_INVENTORY.sha256 | 216 ++ .../include/components/zucchini/buildflags.h | 12 + native/zucchini/include/gtest/gtest_prod.h | 5 + native/zucchini/src/zucchini_ffi.cc | 353 +++ native/zucchini/src/zucchini_ffi.h | 39 + .../vendor/libchrome/MODULE_LICENSE_BSD | 0 native/zucchini/vendor/libchrome/NOTICE | 27 + .../zucchini/vendor/libchrome/base/at_exit.cc | 109 + .../zucchini/vendor/libchrome/base/at_exit.h | 80 + .../vendor/libchrome/base/atomic_ref_count.h | 67 + .../libchrome/base/atomic_sequence_num.h | 33 + .../vendor/libchrome/base/atomicops.h | 161 ++ .../base/atomicops_internals_portable.h | 229 ++ .../vendor/libchrome/base/base_export.h | 29 + .../vendor/libchrome/base/base_switches.h | 51 + native/zucchini/vendor/libchrome/base/bind.h | 482 +++++ .../vendor/libchrome/base/bind_internal.h | 971 +++++++++ .../zucchini/vendor/libchrome/base/callback.h | 146 ++ .../vendor/libchrome/base/callback_forward.h | 27 + .../vendor/libchrome/base/callback_helpers.h | 104 + .../libchrome/base/callback_internal.cc | 84 + .../vendor/libchrome/base/callback_internal.h | 165 ++ .../vendor/libchrome/base/cfi_buildflags.h | 7 + .../vendor/libchrome/base/command_line.h | 247 +++ .../vendor/libchrome/base/compiler_specific.h | 236 ++ .../base/containers/circular_deque.h | 1111 ++++++++++ .../vendor/libchrome/base/containers/queue.h | 23 + .../vendor/libchrome/base/containers/span.h | 453 ++++ .../vendor/libchrome/base/containers/stack.h | 23 + .../libchrome/base/containers/vector_buffer.h | 163 ++ .../libchrome/base/debug/activity_tracker.cc | 1828 ++++++++++++++++ .../libchrome/base/debug/activity_tracker.h | 1367 ++++++++++++ .../vendor/libchrome/base/debug/alias.cc | 27 + .../vendor/libchrome/base/debug/alias.h | 43 + .../vendor/libchrome/base/debug/debugger.h | 44 + .../libchrome/base/debug/debugger_posix.cc | 272 +++ .../base/debug/debugging_buildflags.h | 12 + .../libchrome/base/debug/leak_annotations.h | 46 + .../libchrome/base/debug/proc_maps_linux.h | 94 + .../libchrome/base/debug/stack_trace.cc | 277 +++ .../vendor/libchrome/base/debug/stack_trace.h | 197 ++ .../libchrome/base/debug/stack_trace_posix.cc | 898 ++++++++ .../vendor/libchrome/base/environment.h | 90 + .../vendor/libchrome/base/feature_list.h | 310 +++ .../libchrome/base/file_descriptor_posix.h | 59 + .../vendor/libchrome/base/files/file.h | 381 ++++ .../vendor/libchrome/base/files/file_path.h | 481 +++++ .../libchrome/base/files/file_tracing.h | 95 + .../vendor/libchrome/base/files/file_util.h | 498 +++++ .../libchrome/base/files/memory_mapped_file.h | 136 ++ .../libchrome/base/files/platform_file.h | 43 + .../vendor/libchrome/base/files/scoped_file.h | 62 + .../vendor/libchrome/base/format_macros.h | 97 + .../vendor/libchrome/base/gtest_prod_util.h | 66 + native/zucchini/vendor/libchrome/base/hash.h | 69 + .../vendor/libchrome/base/lazy_instance.h | 210 ++ .../libchrome/base/lazy_instance_helpers.cc | 64 + .../libchrome/base/lazy_instance_helpers.h | 101 + .../vendor/libchrome/base/location.cc | 77 + .../zucchini/vendor/libchrome/base/location.h | 117 + .../zucchini/vendor/libchrome/base/logging.cc | 1065 ++++++++++ .../zucchini/vendor/libchrome/base/logging.h | 1179 ++++++++++ .../zucchini/vendor/libchrome/base/macros.h | 114 + .../libchrome/base/memory/free_deleter.h | 25 + .../vendor/libchrome/base/memory/ptr_util.h | 23 + .../raw_scoped_refptr_mismatch_checker.h | 52 + .../libchrome/base/memory/ref_counted.h | 425 ++++ .../libchrome/base/memory/scoped_refptr.h | 337 +++ .../libchrome/base/memory/shared_memory.h | 254 +++ .../base/memory/shared_memory_handle.h | 238 +++ .../vendor/libchrome/base/memory/singleton.h | 262 +++ .../vendor/libchrome/base/memory/weak_ptr.h | 377 ++++ .../libchrome/base/metrics/bucket_ranges.h | 105 + .../libchrome/base/metrics/field_trial.h | 802 +++++++ .../vendor/libchrome/base/metrics/histogram.h | 610 ++++++ .../libchrome/base/metrics/histogram_base.h | 307 +++ .../base/metrics/histogram_functions.h | 158 ++ .../libchrome/base/metrics/histogram_macros.h | 382 ++++ .../base/metrics/histogram_macros_internal.h | 268 +++ .../base/metrics/histogram_macros_local.h | 90 + .../base/metrics/histogram_samples.h | 267 +++ .../metrics/persistent_memory_allocator.cc | 1204 +++++++++++ .../metrics/persistent_memory_allocator.h | 872 ++++++++ .../libchrome/base/metrics/sparse_histogram.h | 108 + .../vendor/libchrome/base/no_destructor.h | 99 + .../libchrome/base/numerics/checked_math.h | 393 ++++ .../base/numerics/checked_math_impl.h | 567 +++++ .../libchrome/base/numerics/clamped_math.h | 262 +++ .../base/numerics/clamped_math_impl.h | 341 +++ .../base/numerics/safe_conversions.h | 344 +++ .../base/numerics/safe_conversions_impl.h | 850 ++++++++ .../libchrome/base/numerics/safe_math.h | 12 + .../base/numerics/safe_math_clang_gcc_impl.h | 157 ++ .../base/numerics/safe_math_shared_impl.h | 237 +++ .../vendor/libchrome/base/observer_list.h | 307 +++ .../libchrome/base/observer_list_threadsafe.h | 237 +++ .../zucchini/vendor/libchrome/base/optional.h | 922 ++++++++ .../vendor/libchrome/base/pending_task.h | 68 + .../zucchini/vendor/libchrome/base/pickle.h | 345 +++ .../libchrome/base/posix/eintr_wrapper.h | 68 + .../base/posix/file_descriptor_shuffle.h | 87 + .../libchrome/base/posix/safe_strerror.h | 44 + .../vendor/libchrome/base/process/launch.h | 396 ++++ .../vendor/libchrome/base/process/process.h | 213 ++ .../libchrome/base/process/process_handle.h | 103 + .../libchrome/base/scoped_clear_errno.h | 34 + .../vendor/libchrome/base/scoped_generic.h | 189 ++ .../vendor/libchrome/base/sequence_checker.h | 88 + .../libchrome/base/sequence_checker_impl.h | 48 + .../libchrome/base/sequenced_task_runner.h | 176 ++ .../base/sequenced_task_runner_helpers.h | 42 + .../base/single_thread_task_runner.h | 36 + .../zucchini/vendor/libchrome/base/stl_util.h | 410 ++++ .../libchrome/base/strings/char_traits.h | 92 + .../vendor/libchrome/base/strings/string16.h | 219 ++ .../base/strings/string_number_conversions.h | 166 ++ .../libchrome/base/strings/string_piece.cc | 453 ++++ .../libchrome/base/strings/string_piece.h | 483 +++++ .../base/strings/string_piece_forward.h | 24 + .../libchrome/base/strings/string_util.cc | 1124 ++++++++++ .../libchrome/base/strings/string_util.h | 485 +++++ .../base/strings/string_util_posix.h | 37 + .../libchrome/base/strings/stringprintf.cc | 189 ++ .../libchrome/base/strings/stringprintf.h | 66 + .../base/strings/sys_string_conversions.h | 84 + .../strings/utf_string_conversion_utils.h | 100 + .../base/strings/utf_string_conversions.h | 54 + .../libchrome/base/synchronization/lock.h | 151 ++ .../base/synchronization/lock_impl.h | 78 + .../base/synchronization/lock_impl_posix.cc | 133 ++ .../synchronization_buildflags.h | 4 + .../zucchini/vendor/libchrome/base/sys_info.h | 179 ++ .../vendor/libchrome/base/task_runner.h | 154 ++ .../vendor/libchrome/base/template_util.h | 154 ++ .../libchrome/base/third_party/icu/LICENSE | 76 + .../libchrome/base/third_party/icu/icu_utf.h | 442 ++++ .../libchrome/base/third_party/nspr/LICENSE | 35 + .../base/third_party/nspr/MPL-1.1.txt | 469 ++++ .../libchrome/base/third_party/nspr/prtime.h | 263 +++ .../base/threading/platform_thread.h | 240 +++ .../platform_thread_internal_posix.h | 48 + .../base/threading/platform_thread_posix.cc | 305 +++ .../threading/sequenced_task_runner_handle.h | 43 + .../base/threading/thread_collision_warner.h | 245 +++ .../base/threading/thread_id_name_manager.h | 80 + .../libchrome/base/threading/thread_local.h | 99 + .../base/threading/thread_local_storage.cc | 408 ++++ .../base/threading/thread_local_storage.h | 184 ++ .../base/threading/thread_restrictions.h | 536 +++++ .../threading/thread_task_runner_handle.h | 57 + .../vendor/libchrome/base/time/time.cc | 420 ++++ .../vendor/libchrome/base/time/time.h | 993 +++++++++ .../libchrome/base/time/time_now_posix.cc | 123 ++ .../libchrome/base/time/time_override.h | 74 + native/zucchini/vendor/libchrome/base/token.h | 72 + .../vendor/libchrome/base/unguessable_token.h | 116 + native/zucchini/vendor/libchrome/base/vlog.h | 77 + .../vendor/libchrome/build/build_config.h | 263 +++ .../testing/gtest/include/gtest/gtest_prod.h | 1 + native/zucchini/vendor/zucchini/LICENSE | 27 + .../vendor/zucchini/MODULE_LICENSE_CHROME | 0 .../zucchini/vendor/zucchini/abs32_utils.cc | 211 ++ .../vendor/zucchini/address_translator.cc | 258 +++ .../vendor/zucchini/aosp/include/base/check.h | 22 + .../zucchini/aosp/include/base/check_op.h | 24 + .../include/base/containers/cxx20_erase.h | 22 + .../zucchini/aosp/include/build/buildflag.h | 47 + .../include/components/zucchini/abs32_utils.h | 142 ++ .../components/zucchini/address_translator.h | 199 ++ .../include/components/zucchini/algorithm.h | 147 ++ .../include/components/zucchini/arm_utils.h | 423 ++++ .../include/components/zucchini/buffer_sink.h | 68 + .../components/zucchini/buffer_source.h | 141 ++ .../include/components/zucchini/buffer_view.h | 217 ++ .../aosp/include/components/zucchini/crc32.h | 17 + .../components/zucchini/disassembler.h | 155 ++ .../components/zucchini/disassembler_dex.h | 295 +++ .../components/zucchini/disassembler_elf.h | 356 ++++ .../components/zucchini/disassembler_no_op.h | 41 + .../components/zucchini/element_detection.h | 62 + .../components/zucchini/encoded_view.h | 185 ++ .../components/zucchini/equivalence_map.h | 208 ++ .../include/components/zucchini/image_index.h | 116 + .../include/components/zucchini/image_utils.h | 225 ++ .../include/components/zucchini/io_utils.h | 144 ++ .../components/zucchini/patch_reader.h | 285 +++ .../include/components/zucchini/patch_utils.h | 139 ++ .../components/zucchini/patch_writer.h | 272 +++ .../components/zucchini/reference_set.h | 64 + .../components/zucchini/rel32_finder.h | 284 +++ .../include/components/zucchini/rel32_utils.h | 184 ++ .../include/components/zucchini/reloc_elf.h | 102 + .../components/zucchini/suffix_array.h | 475 +++++ .../include/components/zucchini/target_pool.h | 81 + .../components/zucchini/targets_affinity.h | 74 + .../include/components/zucchini/type_dex.h | 332 +++ .../include/components/zucchini/type_elf.h | 283 +++ .../include/components/zucchini/typed_value.h | 57 + .../components/zucchini/version_info.h | 30 + .../include/components/zucchini/zucchini.h | 72 + .../components/zucchini/zucchini_apply.h | 41 + native/zucchini/vendor/zucchini/arm_utils.cc | 597 ++++++ .../zucchini/vendor/zucchini/buffer_source.cc | 105 + native/zucchini/vendor/zucchini/crc32.cc | 43 + .../zucchini/vendor/zucchini/disassembler.cc | 52 + .../vendor/zucchini/disassembler_dex.cc | 1890 +++++++++++++++++ .../vendor/zucchini/disassembler_elf.cc | 758 +++++++ .../vendor/zucchini/disassembler_no_op.cc | 31 + .../vendor/zucchini/element_detection.cc | 200 ++ .../vendor/zucchini/equivalence_map.cc | 549 +++++ .../zucchini/vendor/zucchini/patch_reader.cc | 401 ++++ .../zucchini/vendor/zucchini/rel32_finder.cc | 294 +++ .../zucchini/vendor/zucchini/rel32_utils.cc | 67 + native/zucchini/vendor/zucchini/reloc_elf.cc | 163 ++ .../zucchini/vendor/zucchini/target_pool.cc | 84 + .../vendor/zucchini/zucchini_apply.cc | 217 ++ src/lib.rs | 1 + src/zucchini.rs | 135 ++ tests/delta_extraction.rs | 50 + tests/fixtures/zucchini/LICENSE | 29 + tests/fixtures/zucchini/README.md | 22 + tests/fixtures/zucchini/SHA256SUMS | 22 + tests/fixtures/zucchini/dex-large-new.dex | Bin 0 -> 786788 bytes tests/fixtures/zucchini/dex-large-old.dex | Bin 0 -> 786788 bytes .../fixtures/zucchini/dex-large-unsafe16.zuc | Bin 0 -> 65758 bytes tests/fixtures/zucchini/dex-large.zuc | Bin 0 -> 65758 bytes tests/fixtures/zucchini/dex-new.dex | Bin 0 -> 400 bytes tests/fixtures/zucchini/dex-old.dex | Bin 0 -> 400 bytes tests/fixtures/zucchini/dex.zuc | Bin 0 -> 223 bytes tests/fixtures/zucchini/elf-arm32-new | Bin 0 -> 280 bytes tests/fixtures/zucchini/elf-arm32-old | Bin 0 -> 280 bytes tests/fixtures/zucchini/elf-arm32.zuc | Bin 0 -> 103 bytes tests/fixtures/zucchini/elf-arm64-new | Bin 0 -> 384 bytes tests/fixtures/zucchini/elf-arm64-old | Bin 0 -> 384 bytes tests/fixtures/zucchini/elf-arm64.zuc | Bin 0 -> 104 bytes tests/fixtures/zucchini/elf-new | Bin 0 -> 4680 bytes tests/fixtures/zucchini/elf-old | Bin 0 -> 4680 bytes tests/fixtures/zucchini/elf-x86-new | Bin 0 -> 280 bytes tests/fixtures/zucchini/elf-x86-old | Bin 0 -> 280 bytes tests/fixtures/zucchini/elf-x86.zuc | Bin 0 -> 103 bytes tests/fixtures/zucchini/elf.zuc | Bin 0 -> 112 bytes tests/fixtures/zucchini/noop-new.bin | 1 + tests/fixtures/zucchini/noop-old.bin | 1 + tests/fixtures/zucchini/noop.zuc | Bin 0 -> 88 bytes 249 files changed, 52865 insertions(+) create mode 100644 native/zucchini/README.md create mode 100644 native/zucchini/SOURCE_INVENTORY.sha256 create mode 100644 native/zucchini/include/components/zucchini/buildflags.h create mode 100644 native/zucchini/include/gtest/gtest_prod.h create mode 100644 native/zucchini/src/zucchini_ffi.cc create mode 100644 native/zucchini/src/zucchini_ffi.h create mode 100644 native/zucchini/vendor/libchrome/MODULE_LICENSE_BSD create mode 100644 native/zucchini/vendor/libchrome/NOTICE create mode 100644 native/zucchini/vendor/libchrome/base/at_exit.cc create mode 100644 native/zucchini/vendor/libchrome/base/at_exit.h create mode 100644 native/zucchini/vendor/libchrome/base/atomic_ref_count.h create mode 100644 native/zucchini/vendor/libchrome/base/atomic_sequence_num.h create mode 100644 native/zucchini/vendor/libchrome/base/atomicops.h create mode 100644 native/zucchini/vendor/libchrome/base/atomicops_internals_portable.h create mode 100644 native/zucchini/vendor/libchrome/base/base_export.h create mode 100644 native/zucchini/vendor/libchrome/base/base_switches.h create mode 100644 native/zucchini/vendor/libchrome/base/bind.h create mode 100644 native/zucchini/vendor/libchrome/base/bind_internal.h create mode 100644 native/zucchini/vendor/libchrome/base/callback.h create mode 100644 native/zucchini/vendor/libchrome/base/callback_forward.h create mode 100644 native/zucchini/vendor/libchrome/base/callback_helpers.h create mode 100644 native/zucchini/vendor/libchrome/base/callback_internal.cc create mode 100644 native/zucchini/vendor/libchrome/base/callback_internal.h create mode 100644 native/zucchini/vendor/libchrome/base/cfi_buildflags.h create mode 100644 native/zucchini/vendor/libchrome/base/command_line.h create mode 100644 native/zucchini/vendor/libchrome/base/compiler_specific.h create mode 100644 native/zucchini/vendor/libchrome/base/containers/circular_deque.h create mode 100644 native/zucchini/vendor/libchrome/base/containers/queue.h create mode 100644 native/zucchini/vendor/libchrome/base/containers/span.h create mode 100644 native/zucchini/vendor/libchrome/base/containers/stack.h create mode 100644 native/zucchini/vendor/libchrome/base/containers/vector_buffer.h create mode 100644 native/zucchini/vendor/libchrome/base/debug/activity_tracker.cc create mode 100644 native/zucchini/vendor/libchrome/base/debug/activity_tracker.h create mode 100644 native/zucchini/vendor/libchrome/base/debug/alias.cc create mode 100644 native/zucchini/vendor/libchrome/base/debug/alias.h create mode 100644 native/zucchini/vendor/libchrome/base/debug/debugger.h create mode 100644 native/zucchini/vendor/libchrome/base/debug/debugger_posix.cc create mode 100644 native/zucchini/vendor/libchrome/base/debug/debugging_buildflags.h create mode 100644 native/zucchini/vendor/libchrome/base/debug/leak_annotations.h create mode 100644 native/zucchini/vendor/libchrome/base/debug/proc_maps_linux.h create mode 100644 native/zucchini/vendor/libchrome/base/debug/stack_trace.cc create mode 100644 native/zucchini/vendor/libchrome/base/debug/stack_trace.h create mode 100644 native/zucchini/vendor/libchrome/base/debug/stack_trace_posix.cc create mode 100644 native/zucchini/vendor/libchrome/base/environment.h create mode 100644 native/zucchini/vendor/libchrome/base/feature_list.h create mode 100644 native/zucchini/vendor/libchrome/base/file_descriptor_posix.h create mode 100644 native/zucchini/vendor/libchrome/base/files/file.h create mode 100644 native/zucchini/vendor/libchrome/base/files/file_path.h create mode 100644 native/zucchini/vendor/libchrome/base/files/file_tracing.h create mode 100644 native/zucchini/vendor/libchrome/base/files/file_util.h create mode 100644 native/zucchini/vendor/libchrome/base/files/memory_mapped_file.h create mode 100644 native/zucchini/vendor/libchrome/base/files/platform_file.h create mode 100644 native/zucchini/vendor/libchrome/base/files/scoped_file.h create mode 100644 native/zucchini/vendor/libchrome/base/format_macros.h create mode 100644 native/zucchini/vendor/libchrome/base/gtest_prod_util.h create mode 100644 native/zucchini/vendor/libchrome/base/hash.h create mode 100644 native/zucchini/vendor/libchrome/base/lazy_instance.h create mode 100644 native/zucchini/vendor/libchrome/base/lazy_instance_helpers.cc create mode 100644 native/zucchini/vendor/libchrome/base/lazy_instance_helpers.h create mode 100644 native/zucchini/vendor/libchrome/base/location.cc create mode 100644 native/zucchini/vendor/libchrome/base/location.h create mode 100644 native/zucchini/vendor/libchrome/base/logging.cc create mode 100644 native/zucchini/vendor/libchrome/base/logging.h create mode 100644 native/zucchini/vendor/libchrome/base/macros.h create mode 100644 native/zucchini/vendor/libchrome/base/memory/free_deleter.h create mode 100644 native/zucchini/vendor/libchrome/base/memory/ptr_util.h create mode 100644 native/zucchini/vendor/libchrome/base/memory/raw_scoped_refptr_mismatch_checker.h create mode 100644 native/zucchini/vendor/libchrome/base/memory/ref_counted.h create mode 100644 native/zucchini/vendor/libchrome/base/memory/scoped_refptr.h create mode 100644 native/zucchini/vendor/libchrome/base/memory/shared_memory.h create mode 100644 native/zucchini/vendor/libchrome/base/memory/shared_memory_handle.h create mode 100644 native/zucchini/vendor/libchrome/base/memory/singleton.h create mode 100644 native/zucchini/vendor/libchrome/base/memory/weak_ptr.h create mode 100644 native/zucchini/vendor/libchrome/base/metrics/bucket_ranges.h create mode 100644 native/zucchini/vendor/libchrome/base/metrics/field_trial.h create mode 100644 native/zucchini/vendor/libchrome/base/metrics/histogram.h create mode 100644 native/zucchini/vendor/libchrome/base/metrics/histogram_base.h create mode 100644 native/zucchini/vendor/libchrome/base/metrics/histogram_functions.h create mode 100644 native/zucchini/vendor/libchrome/base/metrics/histogram_macros.h create mode 100644 native/zucchini/vendor/libchrome/base/metrics/histogram_macros_internal.h create mode 100644 native/zucchini/vendor/libchrome/base/metrics/histogram_macros_local.h create mode 100644 native/zucchini/vendor/libchrome/base/metrics/histogram_samples.h create mode 100644 native/zucchini/vendor/libchrome/base/metrics/persistent_memory_allocator.cc create mode 100644 native/zucchini/vendor/libchrome/base/metrics/persistent_memory_allocator.h create mode 100644 native/zucchini/vendor/libchrome/base/metrics/sparse_histogram.h create mode 100644 native/zucchini/vendor/libchrome/base/no_destructor.h create mode 100644 native/zucchini/vendor/libchrome/base/numerics/checked_math.h create mode 100644 native/zucchini/vendor/libchrome/base/numerics/checked_math_impl.h create mode 100644 native/zucchini/vendor/libchrome/base/numerics/clamped_math.h create mode 100644 native/zucchini/vendor/libchrome/base/numerics/clamped_math_impl.h create mode 100644 native/zucchini/vendor/libchrome/base/numerics/safe_conversions.h create mode 100644 native/zucchini/vendor/libchrome/base/numerics/safe_conversions_impl.h create mode 100644 native/zucchini/vendor/libchrome/base/numerics/safe_math.h create mode 100644 native/zucchini/vendor/libchrome/base/numerics/safe_math_clang_gcc_impl.h create mode 100644 native/zucchini/vendor/libchrome/base/numerics/safe_math_shared_impl.h create mode 100644 native/zucchini/vendor/libchrome/base/observer_list.h create mode 100644 native/zucchini/vendor/libchrome/base/observer_list_threadsafe.h create mode 100644 native/zucchini/vendor/libchrome/base/optional.h create mode 100644 native/zucchini/vendor/libchrome/base/pending_task.h create mode 100644 native/zucchini/vendor/libchrome/base/pickle.h create mode 100644 native/zucchini/vendor/libchrome/base/posix/eintr_wrapper.h create mode 100644 native/zucchini/vendor/libchrome/base/posix/file_descriptor_shuffle.h create mode 100644 native/zucchini/vendor/libchrome/base/posix/safe_strerror.h create mode 100644 native/zucchini/vendor/libchrome/base/process/launch.h create mode 100644 native/zucchini/vendor/libchrome/base/process/process.h create mode 100644 native/zucchini/vendor/libchrome/base/process/process_handle.h create mode 100644 native/zucchini/vendor/libchrome/base/scoped_clear_errno.h create mode 100644 native/zucchini/vendor/libchrome/base/scoped_generic.h create mode 100644 native/zucchini/vendor/libchrome/base/sequence_checker.h create mode 100644 native/zucchini/vendor/libchrome/base/sequence_checker_impl.h create mode 100644 native/zucchini/vendor/libchrome/base/sequenced_task_runner.h create mode 100644 native/zucchini/vendor/libchrome/base/sequenced_task_runner_helpers.h create mode 100644 native/zucchini/vendor/libchrome/base/single_thread_task_runner.h create mode 100644 native/zucchini/vendor/libchrome/base/stl_util.h create mode 100644 native/zucchini/vendor/libchrome/base/strings/char_traits.h create mode 100644 native/zucchini/vendor/libchrome/base/strings/string16.h create mode 100644 native/zucchini/vendor/libchrome/base/strings/string_number_conversions.h create mode 100644 native/zucchini/vendor/libchrome/base/strings/string_piece.cc create mode 100644 native/zucchini/vendor/libchrome/base/strings/string_piece.h create mode 100644 native/zucchini/vendor/libchrome/base/strings/string_piece_forward.h create mode 100644 native/zucchini/vendor/libchrome/base/strings/string_util.cc create mode 100644 native/zucchini/vendor/libchrome/base/strings/string_util.h create mode 100644 native/zucchini/vendor/libchrome/base/strings/string_util_posix.h create mode 100644 native/zucchini/vendor/libchrome/base/strings/stringprintf.cc create mode 100644 native/zucchini/vendor/libchrome/base/strings/stringprintf.h create mode 100644 native/zucchini/vendor/libchrome/base/strings/sys_string_conversions.h create mode 100644 native/zucchini/vendor/libchrome/base/strings/utf_string_conversion_utils.h create mode 100644 native/zucchini/vendor/libchrome/base/strings/utf_string_conversions.h create mode 100644 native/zucchini/vendor/libchrome/base/synchronization/lock.h create mode 100644 native/zucchini/vendor/libchrome/base/synchronization/lock_impl.h create mode 100644 native/zucchini/vendor/libchrome/base/synchronization/lock_impl_posix.cc create mode 100644 native/zucchini/vendor/libchrome/base/synchronization/synchronization_buildflags.h create mode 100644 native/zucchini/vendor/libchrome/base/sys_info.h create mode 100644 native/zucchini/vendor/libchrome/base/task_runner.h create mode 100644 native/zucchini/vendor/libchrome/base/template_util.h create mode 100644 native/zucchini/vendor/libchrome/base/third_party/icu/LICENSE create mode 100644 native/zucchini/vendor/libchrome/base/third_party/icu/icu_utf.h create mode 100644 native/zucchini/vendor/libchrome/base/third_party/nspr/LICENSE create mode 100644 native/zucchini/vendor/libchrome/base/third_party/nspr/MPL-1.1.txt create mode 100644 native/zucchini/vendor/libchrome/base/third_party/nspr/prtime.h create mode 100644 native/zucchini/vendor/libchrome/base/threading/platform_thread.h create mode 100644 native/zucchini/vendor/libchrome/base/threading/platform_thread_internal_posix.h create mode 100644 native/zucchini/vendor/libchrome/base/threading/platform_thread_posix.cc create mode 100644 native/zucchini/vendor/libchrome/base/threading/sequenced_task_runner_handle.h create mode 100644 native/zucchini/vendor/libchrome/base/threading/thread_collision_warner.h create mode 100644 native/zucchini/vendor/libchrome/base/threading/thread_id_name_manager.h create mode 100644 native/zucchini/vendor/libchrome/base/threading/thread_local.h create mode 100644 native/zucchini/vendor/libchrome/base/threading/thread_local_storage.cc create mode 100644 native/zucchini/vendor/libchrome/base/threading/thread_local_storage.h create mode 100644 native/zucchini/vendor/libchrome/base/threading/thread_restrictions.h create mode 100644 native/zucchini/vendor/libchrome/base/threading/thread_task_runner_handle.h create mode 100644 native/zucchini/vendor/libchrome/base/time/time.cc create mode 100644 native/zucchini/vendor/libchrome/base/time/time.h create mode 100644 native/zucchini/vendor/libchrome/base/time/time_now_posix.cc create mode 100644 native/zucchini/vendor/libchrome/base/time/time_override.h create mode 100644 native/zucchini/vendor/libchrome/base/token.h create mode 100644 native/zucchini/vendor/libchrome/base/unguessable_token.h create mode 100644 native/zucchini/vendor/libchrome/base/vlog.h create mode 100644 native/zucchini/vendor/libchrome/build/build_config.h create mode 100644 native/zucchini/vendor/libchrome/testing/gtest/include/gtest/gtest_prod.h create mode 100644 native/zucchini/vendor/zucchini/LICENSE create mode 100644 native/zucchini/vendor/zucchini/MODULE_LICENSE_CHROME create mode 100644 native/zucchini/vendor/zucchini/abs32_utils.cc create mode 100644 native/zucchini/vendor/zucchini/address_translator.cc create mode 100644 native/zucchini/vendor/zucchini/aosp/include/base/check.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/base/check_op.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/base/containers/cxx20_erase.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/build/buildflag.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/abs32_utils.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/address_translator.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/algorithm.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/arm_utils.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/buffer_sink.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/buffer_source.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/buffer_view.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/crc32.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/disassembler.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/disassembler_dex.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/disassembler_elf.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/disassembler_no_op.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/element_detection.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/encoded_view.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/equivalence_map.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/image_index.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/image_utils.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/io_utils.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/patch_reader.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/patch_utils.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/patch_writer.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/reference_set.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/rel32_finder.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/rel32_utils.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/reloc_elf.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/suffix_array.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/target_pool.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/targets_affinity.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/type_dex.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/type_elf.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/typed_value.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/version_info.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/zucchini.h create mode 100644 native/zucchini/vendor/zucchini/aosp/include/components/zucchini/zucchini_apply.h create mode 100644 native/zucchini/vendor/zucchini/arm_utils.cc create mode 100644 native/zucchini/vendor/zucchini/buffer_source.cc create mode 100644 native/zucchini/vendor/zucchini/crc32.cc create mode 100644 native/zucchini/vendor/zucchini/disassembler.cc create mode 100644 native/zucchini/vendor/zucchini/disassembler_dex.cc create mode 100644 native/zucchini/vendor/zucchini/disassembler_elf.cc create mode 100644 native/zucchini/vendor/zucchini/disassembler_no_op.cc create mode 100644 native/zucchini/vendor/zucchini/element_detection.cc create mode 100644 native/zucchini/vendor/zucchini/equivalence_map.cc create mode 100644 native/zucchini/vendor/zucchini/patch_reader.cc create mode 100644 native/zucchini/vendor/zucchini/rel32_finder.cc create mode 100644 native/zucchini/vendor/zucchini/rel32_utils.cc create mode 100644 native/zucchini/vendor/zucchini/reloc_elf.cc create mode 100644 native/zucchini/vendor/zucchini/target_pool.cc create mode 100644 native/zucchini/vendor/zucchini/zucchini_apply.cc create mode 100644 src/zucchini.rs create mode 100644 tests/fixtures/zucchini/LICENSE create mode 100644 tests/fixtures/zucchini/README.md create mode 100644 tests/fixtures/zucchini/SHA256SUMS create mode 100644 tests/fixtures/zucchini/dex-large-new.dex create mode 100644 tests/fixtures/zucchini/dex-large-old.dex create mode 100644 tests/fixtures/zucchini/dex-large-unsafe16.zuc create mode 100644 tests/fixtures/zucchini/dex-large.zuc create mode 100644 tests/fixtures/zucchini/dex-new.dex create mode 100644 tests/fixtures/zucchini/dex-old.dex create mode 100644 tests/fixtures/zucchini/dex.zuc create mode 100644 tests/fixtures/zucchini/elf-arm32-new create mode 100644 tests/fixtures/zucchini/elf-arm32-old create mode 100644 tests/fixtures/zucchini/elf-arm32.zuc create mode 100644 tests/fixtures/zucchini/elf-arm64-new create mode 100644 tests/fixtures/zucchini/elf-arm64-old create mode 100644 tests/fixtures/zucchini/elf-arm64.zuc create mode 100755 tests/fixtures/zucchini/elf-new create mode 100755 tests/fixtures/zucchini/elf-old create mode 100644 tests/fixtures/zucchini/elf-x86-new create mode 100644 tests/fixtures/zucchini/elf-x86-old create mode 100644 tests/fixtures/zucchini/elf-x86.zuc create mode 100644 tests/fixtures/zucchini/elf.zuc create mode 100644 tests/fixtures/zucchini/noop-new.bin create mode 100644 tests/fixtures/zucchini/noop-old.bin create mode 100644 tests/fixtures/zucchini/noop.zuc diff --git a/.gitignore b/.gitignore index 43d1f25..1e54029 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ # Generated by Cargo # will have compiled files and executables debug/ +!native/zucchini/vendor/libchrome/base/debug/ +!native/zucchini/vendor/libchrome/base/debug/** dist/ target/ diff --git a/Cargo.lock b/Cargo.lock index a550f90..75f87b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -635,6 +635,7 @@ dependencies = [ "brotli", "bsdiff-android", "bzip2", + "cc", "clap", "color-print", "ctrlc", diff --git a/Cargo.toml b/Cargo.toml index 1920a7b..06fb104 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ crate-type = ["cdylib", "rlib"] python = ["dep:pyo3"] [build-dependencies] +cc = "=1.2.27" prost-build = "0.14.1" protoc-bin-vendored = "3.2.0" diff --git a/build.rs b/build.rs index 38cec17..9060e65 100644 --- a/build.rs +++ b/build.rs @@ -1,4 +1,57 @@ +use std::fs; +use std::path::{Path, PathBuf}; + +const ZUCCHINI_SOURCES: &[&str] = &[ + "native/zucchini/src/zucchini_ffi.cc", + "native/zucchini/vendor/zucchini/abs32_utils.cc", + "native/zucchini/vendor/zucchini/address_translator.cc", + "native/zucchini/vendor/zucchini/arm_utils.cc", + "native/zucchini/vendor/zucchini/buffer_source.cc", + "native/zucchini/vendor/zucchini/crc32.cc", + "native/zucchini/vendor/zucchini/disassembler.cc", + "native/zucchini/vendor/zucchini/disassembler_dex.cc", + "native/zucchini/vendor/zucchini/disassembler_elf.cc", + "native/zucchini/vendor/zucchini/disassembler_no_op.cc", + "native/zucchini/vendor/zucchini/element_detection.cc", + "native/zucchini/vendor/zucchini/equivalence_map.cc", + "native/zucchini/vendor/zucchini/patch_reader.cc", + "native/zucchini/vendor/zucchini/rel32_finder.cc", + "native/zucchini/vendor/zucchini/rel32_utils.cc", + "native/zucchini/vendor/zucchini/reloc_elf.cc", + "native/zucchini/vendor/zucchini/target_pool.cc", + "native/zucchini/vendor/zucchini/zucchini_apply.cc", + "native/zucchini/vendor/libchrome/base/at_exit.cc", + "native/zucchini/vendor/libchrome/base/callback_internal.cc", + "native/zucchini/vendor/libchrome/base/debug/activity_tracker.cc", + "native/zucchini/vendor/libchrome/base/debug/alias.cc", + "native/zucchini/vendor/libchrome/base/debug/debugger_posix.cc", + "native/zucchini/vendor/libchrome/base/debug/stack_trace.cc", + "native/zucchini/vendor/libchrome/base/debug/stack_trace_posix.cc", + "native/zucchini/vendor/libchrome/base/lazy_instance_helpers.cc", + "native/zucchini/vendor/libchrome/base/location.cc", + "native/zucchini/vendor/libchrome/base/logging.cc", + "native/zucchini/vendor/libchrome/base/metrics/persistent_memory_allocator.cc", + "native/zucchini/vendor/libchrome/base/strings/string_piece.cc", + "native/zucchini/vendor/libchrome/base/strings/stringprintf.cc", + "native/zucchini/vendor/libchrome/base/strings/string_util.cc", + "native/zucchini/vendor/libchrome/base/synchronization/lock_impl_posix.cc", + "native/zucchini/vendor/libchrome/base/threading/platform_thread_posix.cc", + "native/zucchini/vendor/libchrome/base/threading/thread_local_storage.cc", + "native/zucchini/vendor/libchrome/base/time/time.cc", + "native/zucchini/vendor/libchrome/base/time/time_now_posix.cc", +]; + fn main() { + println!("cargo:rustc-check-cfg=cfg(otadump_zucchini)"); + println!("cargo:rerun-if-changed=src/protos/chromeos_update_engine/update_metadata.proto"); + if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("linux") + && std::env::var("CARGO_CFG_TARGET_ARCH").as_deref() == Ok("x86_64") + && std::env::var("CARGO_CFG_TARGET_ENV").as_deref() == Ok("gnu") + { + build_zucchini(); + println!("cargo:rustc-cfg=otadump_zucchini"); + } + let protoc = protoc_bin_vendored::protoc_bin_path().expect("unable to find vendored protoc"); let mut config = prost_build::Config::new(); config.protoc_executable(protoc); @@ -9,3 +62,72 @@ fn main() { ) .expect("error compiling protobuf files"); } + +fn build_zucchini() { + let root = Path::new("native/zucchini"); + let mut tracked = Vec::new(); + collect_files(root, &mut tracked); + tracked.sort(); + for path in tracked { + println!("cargo:rerun-if-changed={}", path.display()); + } + + let mut build = cc::Build::new(); + build + .cpp(true) + .cpp_link_stdlib(None) + .std("c++17") + .opt_level(2) + .define("NDEBUG", None) + .define("OFFICIAL_BUILD", None) + .define("__ANDROID_HOST__", None) + .define("DONT_EMBED_BUILD_METADATA", None) + .flag("-ffunction-sections") + .flag("-fdata-sections") + .flag("-fno-exceptions") + .flag("-fno-rtti") + .flag("-Wno-deprecated-declarations") + .flag("-Wno-unused-parameter") + .include(root.join("include")) + .include(root.join("src")) + .include(root.join("vendor/zucchini/aosp/include")) + .include(root.join("vendor/zucchini/aosp/include/components")) + .include(root.join("vendor/libchrome")) + .files(ZUCCHINI_SOURCES); + + let compiler = build.get_compiler(); + build.compile("otadump_zucchini"); + add_static_runtime(&compiler, "libstdc++.a", "stdc++"); + add_static_runtime(&compiler, "libgcc.a", "gcc"); + println!("cargo:rustc-link-arg=-static-libgcc"); +} + +fn add_static_runtime(compiler: &cc::Tool, archive: &str, library: &str) { + let output = compiler + .to_command() + .arg(format!("-print-file-name={archive}")) + .output() + .unwrap_or_else(|error| panic!("unable to locate {archive}: {error}")); + assert!(output.status.success(), "unable to locate {archive}"); + let path = + PathBuf::from(String::from_utf8(output.stdout).expect("compiler path is not UTF-8").trim()); + let directory = path + .parent() + .filter(|_| path.is_absolute() && path.is_file()) + .unwrap_or_else(|| panic!("compiler did not provide a usable {archive} path")); + println!("cargo:rustc-link-search=native={}", directory.display()); + println!("cargo:rustc-link-lib=static={library}"); +} + +fn collect_files(directory: &Path, files: &mut Vec) { + let entries = fs::read_dir(directory) + .unwrap_or_else(|error| panic!("unable to read {}: {error}", directory.display())); + for entry in entries { + let path = entry.expect("unable to read native source entry").path(); + if path.is_dir() { + collect_files(&path, files); + } else { + files.push(path); + } + } +} diff --git a/native/zucchini/README.md b/native/zucchini/README.md new file mode 100644 index 0000000..55d6a60 --- /dev/null +++ b/native/zucchini/README.md @@ -0,0 +1,89 @@ +# Zucchini native boundary + +This directory contains the audited apply-only closure used by `otadump` on Linux x86_64. +It does not contain either complete upstream repository, patch generators, command-line tools, tests, binaries, or object files. + +## Source pins + +- AOSP `external/zucchini`: `e256025d9d9c906caba814fd7b99a311ac8b8ec1` +- AOSP `external/libchrome`: `0aba0dccfcd93d8303678a4beb61594f7582c73c` + +`SOURCE_INVENTORY.sha256` lists every vendored file and its digest. +The apply closure contains these upstream Zucchini translation units: + +```text +abs32_utils.cc +address_translator.cc +arm_utils.cc +buffer_source.cc +crc32.cc +disassembler.cc +disassembler_dex.cc +disassembler_elf.cc +disassembler_no_op.cc +element_detection.cc +equivalence_map.cc +patch_reader.cc +rel32_finder.cc +rel32_utils.cc +reloc_elf.cc +target_pool.cc +zucchini_apply.cc +``` + +It contains these upstream libchrome translation units: + +```text +base/at_exit.cc +base/callback_internal.cc +base/debug/activity_tracker.cc +base/debug/alias.cc +base/debug/debugger_posix.cc +base/debug/stack_trace.cc +base/debug/stack_trace_posix.cc +base/lazy_instance_helpers.cc +base/location.cc +base/logging.cc +base/metrics/persistent_memory_allocator.cc +base/strings/string_piece.cc +base/strings/stringprintf.cc +base/strings/string_util.cc +base/synchronization/lock_impl_posix.cc +base/threading/platform_thread_posix.cc +base/threading/thread_local_storage.cc +base/time/time.cc +base/time/time_now_posix.cc +``` + +Headers in the inventory are the compiler-reported transitive header closure for these units and the FFI wrapper. + +## Build configuration + +`build.rs` uses `cc` with the audited release configuration: + +```text +-std=c++17 -O2 -DNDEBUG -DOFFICIAL_BUILD -D__ANDROID_HOST__ +-DDONT_EMBED_BUILD_METADATA -ffunction-sections -fdata-sections +-fno-exceptions -fno-rtti +``` + +The generated `include/components/zucchini/buildflags.h` is copied from the pinned Android build. +It enables DEX and ELF and disables Win32 and ZTF. +`include/gtest/gtest_prod.h` is a declaration-only generated-header substitute used by the proof. +The build links the native C++ and GCC support archives statically. +Rust's Linux GNU target still links its baseline `libgcc_s.so.1`, as an empty Rust binary does, and retains dynamic glibc. + +## Local boundary code + +`src/zucchini_ffi.cc` and `src/zucchini_ffi.h` started as the project-owned Milestone 5a proof boundary. +The local copy changes the exported prefix from `zucchini_proof` to `otadump_zucchini`. +It also extends the audited DEX preflight to allow more than 65,536 strings. +The extension resolves each reference delta without writing and rejects any `const-string` 16-bit writer target above index 65,535 before upstream apply can reach its checked narrowing cast. +The 32-bit and jumbo writers retain the full DEX string range. + +## License notices + +The Zucchini sources use the BSD-style license in `vendor/zucchini/LICENSE` and retain `vendor/zucchini/MODULE_LICENSE_CHROME`. +The libchrome sources retain `vendor/libchrome/NOTICE` and `vendor/libchrome/MODULE_LICENSE_BSD`. +The copied ICU declarations retain `vendor/libchrome/base/third_party/icu/LICENSE`. +The copied NSPR declarations retain their tri-license notice in `vendor/libchrome/base/third_party/nspr/LICENSE` and the canonical full MPL 1.1 text in `vendor/libchrome/base/third_party/nspr/MPL-1.1.txt`. diff --git a/native/zucchini/SOURCE_INVENTORY.sha256 b/native/zucchini/SOURCE_INVENTORY.sha256 new file mode 100644 index 0000000..cdecc7d --- /dev/null +++ b/native/zucchini/SOURCE_INVENTORY.sha256 @@ -0,0 +1,216 @@ +b518d60f01ff369072d4c538af21735c7eadf1e7b4da6672b9d97747cdd90aee ./README.md +46910208394401b1554d4d7f72a29321825a69f56d652757416df82d233fc1ca ./include/components/zucchini/buildflags.h +c70eca6f124dd593ae6315bc4576e7d740776614248302e176bd4facd3fd8f46 ./include/gtest/gtest_prod.h +525112dc60b8361681d6977a74b8a07d06e20fb266ef412e4a11e1a113068fb2 ./src/zucchini_ffi.cc +11696b3b8d3dc920e2375d98fd95028cd32c6c1ea86b082d6f800e00fb357703 ./src/zucchini_ffi.h +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ./vendor/libchrome/MODULE_LICENSE_BSD +7a209dd1b94cabdb5ea9c6f9164b9546ffa5daaa671e7767d49510db055f5c51 ./vendor/libchrome/NOTICE +7eea8279dea1533467acb0e6b21e7506843c75540455b79a1fb08be6b4ffa999 ./vendor/libchrome/base/at_exit.cc +ab949b0cff351bb03783efae73468610a26ef4ae4dcce130a91777df9ea87c16 ./vendor/libchrome/base/at_exit.h +95925ae1ec4daff96d8233d4c0a95405481542b50d58d33cafc2f46dce121e52 ./vendor/libchrome/base/atomic_ref_count.h +ff55aafc7cdfd61ce5fd0f3fdd3deadcfe651171a195984bf2ecef7a6ee51e7e ./vendor/libchrome/base/atomic_sequence_num.h +e11cc9ca44c0d03b7fe7bbb75251f8946f2ff0faec7c34f93784d5bbca41a9af ./vendor/libchrome/base/atomicops.h +66aba23e522d58c43a150bf5b4d27c9fe568518a498ef36e8686bd6ad606ee50 ./vendor/libchrome/base/atomicops_internals_portable.h +a115f8bb46d074a4c882b7ba45d3b9d63b89db80b81c48e334e7da3d31e1b24a ./vendor/libchrome/base/base_export.h +fcaf8cb6e8d7e8844d53e8a79152d313ff7d1fb2faa4bad867a7d629855ba9a0 ./vendor/libchrome/base/base_switches.h +0a5c281222b7ed05e208bb255ec597c6386314d02e57b8f7f871dc9445e9dcc4 ./vendor/libchrome/base/bind.h +3e9d5557534a399d69813cab3bc2b41a7c68a4ea2a9c868979576fcf05933414 ./vendor/libchrome/base/bind_internal.h +14acefaa5942c15a522d943d88422e8f1d6e861998248ed2654b303752434ebf ./vendor/libchrome/base/callback.h +6a3fe03450edca7552622704220e7bfd8b7455b8898e84b0d4c68fda7a8492f3 ./vendor/libchrome/base/callback_forward.h +1f6dd284ea3299ef73ddd1a6c1adc8b83c7192a36cb48ca747a364cba837f533 ./vendor/libchrome/base/callback_helpers.h +0f2c1cbf003f898f298a74ac9c247d52a8b825e2d6d0f251155e43be8f297c2b ./vendor/libchrome/base/callback_internal.cc +f43005e3ae91bc78a6493b7f0ba9e41603bd2c4e5cf079d7f4a3075ccf9e0911 ./vendor/libchrome/base/callback_internal.h +763e91c9dc45aed5f350df32ec0f2ada535e9d36f7e28c9924fa02dcf07e28d4 ./vendor/libchrome/base/cfi_buildflags.h +6256e005c8a1d0a3af1038064b851feacd021605e0017c0e3481512e279c6804 ./vendor/libchrome/base/command_line.h +ddf87b6d8ada67f84b3032ee303ae67f3cac666303e532930af7eb64818fc88f ./vendor/libchrome/base/compiler_specific.h +16be70435e9ea50e74da48e11f40cbddd8d9a508a8ba5ee86fa611b6ed81f183 ./vendor/libchrome/base/containers/circular_deque.h +4d1d402d9b199e292d06dad9ef9fcfdd453d8e6ba6f93c9b305d7e4cc4d7ac06 ./vendor/libchrome/base/containers/queue.h +8b290149ae390c707d5120690b5f7dc016325276ff83e3231fb31e62de5543ca ./vendor/libchrome/base/containers/span.h +d524a463cc85d6c3359d79cd2d8e619ba4894b1820930296115c8685cf8a0615 ./vendor/libchrome/base/containers/stack.h +2095c72078ff48d5cd31fb16f8c131c4b037ba8afdf42e01c21358b069dbd5a3 ./vendor/libchrome/base/containers/vector_buffer.h +5248ace2637fd8a66e235de1e0eec6371b5f5036f360105bccf3c0e824f6ab04 ./vendor/libchrome/base/debug/activity_tracker.cc +660e2682919377aef71bd26189022d665583dcaeff1933666ae82d0dedb2cc22 ./vendor/libchrome/base/debug/activity_tracker.h +7e60b2250a92dddad5c0aff52d0ba7d8988e1662549943334604b6120d4710f9 ./vendor/libchrome/base/debug/alias.cc +19d038abbeb20294dae3dd7bee5f17ec166d981c45e664a3d627a930f860d479 ./vendor/libchrome/base/debug/alias.h +384808eaf4c764a17104d7e42d28ebed58ded5c06cfc1d618e673c4a44959978 ./vendor/libchrome/base/debug/debugger.h +5e7ccee5d6abf15827f1bbb054528af3d3ad105f8bf517ef8d06671bdf913414 ./vendor/libchrome/base/debug/debugger_posix.cc +ef306a4df95ee2e7325dfe59ad00b8f328a5f4a6a83cef95295a7ad32ef9af9c ./vendor/libchrome/base/debug/debugging_buildflags.h +577cc68c01e8fc3195301091d0dea125358d3d1012a735480fa965a79f2775c6 ./vendor/libchrome/base/debug/leak_annotations.h +c0a70a2d084504e5995ced489c99b27699aae553982934a4a8bf45310c86ba31 ./vendor/libchrome/base/debug/proc_maps_linux.h +abbad5021abe5d0610cd527a1c32fbf21ca66b387d66de25eb2754f6cdab8dad ./vendor/libchrome/base/debug/stack_trace.cc +8983e7f6cef3edcf2e61b8dfa1f3ef59801f6ce921967718164b955ef0a1c3be ./vendor/libchrome/base/debug/stack_trace.h +509ccef03a82f241e79dc5eb697c274a099c41459494bc4192cb2c9a33a71e0f ./vendor/libchrome/base/debug/stack_trace_posix.cc +6f1023488e5778f8918240f820a21822995a52bd33e122ad559a06aeb099f9b6 ./vendor/libchrome/base/environment.h +6847915693d962edb7c003221ef28c463b38450fc4fb0778d6ee42ee5b4e392a ./vendor/libchrome/base/feature_list.h +898df95516fd3d653460682a50bac033bd634211007e1aa26522db31993c2425 ./vendor/libchrome/base/file_descriptor_posix.h +6331fcdf07251dd8ff20df9b0b63cc356d9e65b15fae4f0ffd37dc8d865dc21b ./vendor/libchrome/base/files/file.h +abd100566d3872fdb06542f9eba167b825b3dd716969769125d2219152535b49 ./vendor/libchrome/base/files/file_path.h +1edbaf0012fac3c3b0b7750548a400c82ddcb8303fa3e2a0ef1cbec4fc7706e0 ./vendor/libchrome/base/files/file_tracing.h +98e9466692904f0201b6d3ce9ed1655b7a8e7fb9b4fac7794bd9c17823b44038 ./vendor/libchrome/base/files/file_util.h +2c8211841884991837e1f674e9e472e5ce2e33d3a00f3d0e282fd6310d05a140 ./vendor/libchrome/base/files/memory_mapped_file.h +356a0860f98e07c65fb55bab6c1358ce798deeb0926f36c6366d8ce2d50725ae ./vendor/libchrome/base/files/platform_file.h +4bc310531e4c3a0ef738451c12c1c2c8075b2d3b578ca769687f86f28ca82c3c ./vendor/libchrome/base/files/scoped_file.h +c7867f094c7341c1908171982cac96da60a4e8ff636d565bd41f38f331bee0b7 ./vendor/libchrome/base/format_macros.h +39f1ed8adf4d8c130ccf26985fc9e248d50810231d1f337fdcc2fd23cb5c2841 ./vendor/libchrome/base/gtest_prod_util.h +0b29c9dc2964398c0e106032951219bae13038f3752e74c5d4103e65ed43771e ./vendor/libchrome/base/hash.h +faaa17dc769a74fa9d4a3471e318b96cdd13f273bb0389bbbbccdcc127c6fb90 ./vendor/libchrome/base/lazy_instance.h +25edacb2c87e390ce637d53a8fca9238dbeb93bd499775b80630af3f02c20b2a ./vendor/libchrome/base/lazy_instance_helpers.cc +a3c6aeefd21c577d5c943a6e57ba275a0ea9510871a08c10ce2667e42f2a46b7 ./vendor/libchrome/base/lazy_instance_helpers.h +e4448091f421abc708425d6f7edbd3cb54024f624269c2c2365845d72edc21b9 ./vendor/libchrome/base/location.cc +ea21ca73df75d082d8054fff602c3504c0b7e1fe47bb7d61469890eec55d5887 ./vendor/libchrome/base/location.h +db1ed6dd2bd092bfb20f526ac4c1ebfd73b175bcc9c78bcf99d5686f5179c67f ./vendor/libchrome/base/logging.cc +4c16e632e60a9033a0d5bdcc840dbc339001b2f8dff90992213b2ac3655cd566 ./vendor/libchrome/base/logging.h +b8d421b95f0707ebddcb68f73aa9b52018c0918e0745ed537a5079df61640ccd ./vendor/libchrome/base/macros.h +39eee8aa7118e77b59b571cf1275a1e7ab8cf6038be511e3fc239f41ef07f49b ./vendor/libchrome/base/memory/free_deleter.h +12b1c3664472a7e0f9f860f09e204433210a6c466c059d6550a80b37d2c5ea32 ./vendor/libchrome/base/memory/ptr_util.h +0ad0481767efdee6ded70882010b370ab8c440010271216ce6c5ac1485dcee2c ./vendor/libchrome/base/memory/raw_scoped_refptr_mismatch_checker.h +3a8c7bee6566182520d980bdb7c84a2f8115e92e800b6570042ab835947b875f ./vendor/libchrome/base/memory/ref_counted.h +3d76ab6dcc4b61f201e5addb6f7e75104af267fdeb2cf0108fc454bcf1ee4433 ./vendor/libchrome/base/memory/scoped_refptr.h +adb6c67fde3e25c44a8c6167f77b31f925cf888d0f8a6b77715915e549dd7990 ./vendor/libchrome/base/memory/shared_memory.h +7cfd65f746b47c18f17beed5d590f766b6bf1a46842a6981a6aa8546cf79697d ./vendor/libchrome/base/memory/shared_memory_handle.h +6d767a757eeb586fc1db68f531e15e6a25ea411fc4d803f9e581feeeecbcd9ae ./vendor/libchrome/base/memory/singleton.h +d98c1ee76f99ab424c1130d0cdb6f91c115c2840b424884687f0b54cc703ddb1 ./vendor/libchrome/base/memory/weak_ptr.h +53e8213f2af73f0e4589ab80d23044fc9b3c4613eaa942e339311c0153168723 ./vendor/libchrome/base/metrics/bucket_ranges.h +566636ba6da0c61032f573fc00787adecb6749bf090d55a0ae26ae3afcf75745 ./vendor/libchrome/base/metrics/field_trial.h +04bb4fcdeaf28852ddaa9dee4b52121fa773533f7cedb6d16819a0404c8a5425 ./vendor/libchrome/base/metrics/histogram.h +002a9bcb9e23b91e262ba1591a41b4980750ad9c651cc20c40341dabf3df0dd4 ./vendor/libchrome/base/metrics/histogram_base.h +b0a4f1f19a60d420d36e37c4e35a49b09d2dddf185fef2c2ecbc3677ce5b8faf ./vendor/libchrome/base/metrics/histogram_functions.h +d8207dfd23a62e3ee0f8f15d22f905f97893826afbce31f3bd92475366f2b4a1 ./vendor/libchrome/base/metrics/histogram_macros.h +26b160d21fdf9e326661872cf277c833c71f1df079d8b468e2e901a9a476db47 ./vendor/libchrome/base/metrics/histogram_macros_internal.h +f12f2fe5cdc0ad5b3c0f7f0eda55eefda882c4a64730ac7220137ff5e0819f1d ./vendor/libchrome/base/metrics/histogram_macros_local.h +879a70627882d3e5c61d745d3dce90f499575380d159a6af652f29b838ac401b ./vendor/libchrome/base/metrics/histogram_samples.h +950b5200b8b79ad3f17647df2d6be1c9857edf53c6b12ac5a5af711c78197e8d ./vendor/libchrome/base/metrics/persistent_memory_allocator.cc +fddf573151d2bb41823eee2cb6a287f3693907ba7e5aadc5dcce38a414171e73 ./vendor/libchrome/base/metrics/persistent_memory_allocator.h +3128707a2e0d6ceca20e38525dc0f36001fe28cd9f804d6d410e3ecf6e14d7fb ./vendor/libchrome/base/metrics/sparse_histogram.h +7f1973655f80dcf22a56859302666c4f585ec18cb4bd57f2a18e1b8b0f68aec6 ./vendor/libchrome/base/no_destructor.h +0bb2cfd67028f404afe4eecdb25685faa3cefe49580ed6c45d04b4cda119eade ./vendor/libchrome/base/numerics/checked_math.h +6b7c4cdfce938c30e60b842ccdabd126796fd4b099abd7f26326ec4b479d7952 ./vendor/libchrome/base/numerics/checked_math_impl.h +b48654af190a06d2689fa3d74846700e94639f6354d9aaba9f44a502f9617c16 ./vendor/libchrome/base/numerics/clamped_math.h +81ed1886215ce8529a67fba88f9489acaba34eb3d135043484502e9933d125a1 ./vendor/libchrome/base/numerics/clamped_math_impl.h +5cfe96da219c074e6c663663a307da3400ff3929c84331543152e9a2d413aec1 ./vendor/libchrome/base/numerics/safe_conversions.h +5dd3f1a915832056c17acb2d6dd3b090e77b6fe43a6793730dc1b6a74173d285 ./vendor/libchrome/base/numerics/safe_conversions_impl.h +bca10c42ff950f1f1e4cef59c34b48b9ff5f70f248ed12eead48d74fc0e38de2 ./vendor/libchrome/base/numerics/safe_math.h +656227ab097d774ee47371f7ad50c94bd86d9ab83ab43531c57c9c78052417cd ./vendor/libchrome/base/numerics/safe_math_clang_gcc_impl.h +ee8d59a9e7c0b79319787aba29622a1bbaec08987d4b587d4ef91184b7788c5d ./vendor/libchrome/base/numerics/safe_math_shared_impl.h +bb44ae253fa52ccdd2bad7c3a1122a1923d64f341d95ab148397aea97b783950 ./vendor/libchrome/base/observer_list.h +f711d658592ab18b49edaa43672263ee9a4cf116e4891e0fa9b5e314740eb167 ./vendor/libchrome/base/observer_list_threadsafe.h +f3af785ce37e88f8cdb42e76f8a6d27fb09c63d4071aff38926c980c4d5eb66d ./vendor/libchrome/base/optional.h +aedd02ae4884b5ecf2de228e6444eb297f093d566cfd09a7de06e5ef900505dd ./vendor/libchrome/base/pending_task.h +42808892f816366f75f101f8adb830dec6d808c785cefd490d8fa8cc06f343fb ./vendor/libchrome/base/pickle.h +cb298c5e1569f0f85ef07d569a6beb76637ba0310a52cf9741a68690d2f8c7ef ./vendor/libchrome/base/posix/eintr_wrapper.h +8746ddfd8cbc57e67fbde0033b3616d5b868ee23179d2c1846c3a7c01317eadc ./vendor/libchrome/base/posix/file_descriptor_shuffle.h +fdc5328da33867488cc291f4654f9a69502307390f7f9d876739f530016ed9f3 ./vendor/libchrome/base/posix/safe_strerror.h +1b6ef4a5ce4fc7b7bedad53ec48d340be9fff8215f209a03134ecc7612fcc3d6 ./vendor/libchrome/base/process/launch.h +7d0eaedf65343835f38d16c7281cd096491866e8156417c379940feeb316e2db ./vendor/libchrome/base/process/process.h +d45eb69e02ac8d950d8aebc87946dbbb0fc5e927e34302128665cc6c5700c881 ./vendor/libchrome/base/process/process_handle.h +5eef370b91123e0b12854ff383947d9a6bd9e553315d75f8d7f2a394456d3c06 ./vendor/libchrome/base/scoped_clear_errno.h +4f68c4c5758cc579cfdab0405d437c54161e4d3ec2761ea5bdadfbdc9d65619a ./vendor/libchrome/base/scoped_generic.h +863a1fb61810bd5d308439428d5301155030dd2d51a73244c4a75ce07ebc3b35 ./vendor/libchrome/base/sequence_checker.h +3b55182f87a1b4c57979f3fe2fcff4833404ed9be6e75ec0423bc8e253afc537 ./vendor/libchrome/base/sequence_checker_impl.h +0b41692ee85e5781605eb68d0e51e3ca4191cede5bb0c37372cb25633f482ec0 ./vendor/libchrome/base/sequenced_task_runner.h +73aee8f58898e1b0f0e0f713003d753b76f901dba5abb5598333a23f38781afe ./vendor/libchrome/base/sequenced_task_runner_helpers.h +a6123b3785d441a2e3d348954d72c90cce063444195e28fef3e5ee4652cd55d1 ./vendor/libchrome/base/single_thread_task_runner.h +b1d59c6adc27090ae8dce081d7ba4f61e38d7fad34ac435d186c53f838bdb4e3 ./vendor/libchrome/base/stl_util.h +36761f94411e4dea7e10d51d6f73f8d850073fffb2810cdbf61a4cbc9781c5b2 ./vendor/libchrome/base/strings/char_traits.h +e307adafcec337b2c94aebbf0895f9895a10e3144a340de022a591329d3cfd83 ./vendor/libchrome/base/strings/string16.h +d5566a0b4d8a72eff8c29981486a256ac311db017fe83440ca28df91a597064d ./vendor/libchrome/base/strings/string_number_conversions.h +d1a7eb29ef12aac370ce7440fd762a3c895df482bfbfce91fe389f8fc02a207c ./vendor/libchrome/base/strings/string_piece.cc +cabcd4c342b5517ca10ca87822167007909a6eb1ea55c56dbf17b399355f2c8e ./vendor/libchrome/base/strings/string_piece.h +aa31bae42314a2cb199178d5b89344e7e238bb87794fcff05a5f4fb14a234680 ./vendor/libchrome/base/strings/string_piece_forward.h +135247764c1c921b0e29227ea50fa6888fcb1f9df1edae94a634c22a2ef9e2ce ./vendor/libchrome/base/strings/string_util.cc +2742eb72e2b4b24c188428bf4fbbf13abfffa0d7ff36d1a1236686f0e02b1174 ./vendor/libchrome/base/strings/string_util.h +65d8a40c660e94776b8062a3ff99362fc0105438cc6b829a31b3ac0042251eda ./vendor/libchrome/base/strings/string_util_posix.h +dc764a4d57c22571762baa7020b2aade45efd771e48de4df1f61864977015427 ./vendor/libchrome/base/strings/stringprintf.cc +f34b0fce20b11198a643713112ce8d896ddbb4c3a9422e91f6d00a2cd836147a ./vendor/libchrome/base/strings/stringprintf.h +af2490563321b285c2c7c4b5d3f37c3769599b227755b2c5b7dae2fe374147b5 ./vendor/libchrome/base/strings/sys_string_conversions.h +dfac333abfe781d854063c6857f91025d34923267d5c2ce25a264a57b1812d2b ./vendor/libchrome/base/strings/utf_string_conversion_utils.h +d1245a0efe12da92fe31ac87a563143612f4192dd569bc88b22114556399d231 ./vendor/libchrome/base/strings/utf_string_conversions.h +2c27513476338b412cfd0c4ea101568e13d82e147dc034bac9c27a37ba5af2c0 ./vendor/libchrome/base/synchronization/lock.h +8bd9a1409ae597c71970d684ef35569ef75640722b51f5f63a17b04cd3173788 ./vendor/libchrome/base/synchronization/lock_impl.h +2ae402e3a5261b4366f889ee2a7e2819c0ce204cf3cafa2db612d142a84020ed ./vendor/libchrome/base/synchronization/lock_impl_posix.cc +b8b9d2bd6274cdafbecc0dfe305629304ad2ae5294d5732e7753ae12fb3cc497 ./vendor/libchrome/base/synchronization/synchronization_buildflags.h +7e422f463d25ff45bfe4790d9f03db767834391dff615aa1776ef27d03f7eb41 ./vendor/libchrome/base/sys_info.h +7f686442f979e845be8587b7512104f691ee19c2adf7c84d766194d2007dccc1 ./vendor/libchrome/base/task_runner.h +68c12b240d5d3b448ac9284c2c6e97b1433f00306316fb6b11a2d1d2cc2f99a6 ./vendor/libchrome/base/template_util.h +5d85142a5609ad177a2d7a2e7cae060b886b8b42f25c5b9803cf0cb5ee04ad2f ./vendor/libchrome/base/third_party/icu/LICENSE +19bafc2118bc6b45b99c38ce1af08bf8aaee09cf97045d1d98085876b5a1b1b7 ./vendor/libchrome/base/third_party/icu/icu_utf.h +d55a403514532af12dc2fbfb2e41900090a5dd6c7c76c8e4d9b20bcc737eac35 ./vendor/libchrome/base/third_party/nspr/LICENSE +f849fc26a7a99981611a3a370e83078deb617d12a45776d6c4cada4d338be469 ./vendor/libchrome/base/third_party/nspr/MPL-1.1.txt +fde038204e73c962e053ac163369338ffe8ac54fd84e3aa21391b3369fd63aca ./vendor/libchrome/base/third_party/nspr/prtime.h +f4b6e1de49233de43b0ce2500468a2c1f5b8783af3fb7761220f38315bc2df67 ./vendor/libchrome/base/threading/platform_thread.h +136e195f9176bcd14c3a3549ea5d8541094439993a4ace95eba87ee0e02288ef ./vendor/libchrome/base/threading/platform_thread_internal_posix.h +40044c247b6b06af72ff5eb62da18dd37ff2f48031f087ba7ed9f86ead7fc04d ./vendor/libchrome/base/threading/platform_thread_posix.cc +2a9146e0a822daad3282d81c663eed290d0a536a11ea0265ae6262f11630e2dd ./vendor/libchrome/base/threading/sequenced_task_runner_handle.h +b1bdbadeed33ddb77193f0844c32e8baa67a5ae5d5dbe3b9f54cadc6a631b972 ./vendor/libchrome/base/threading/thread_collision_warner.h +09573a81341f073ff72e141847c45445e4b41029bcc54ef469416ce34e7f4e1a ./vendor/libchrome/base/threading/thread_id_name_manager.h +39f1e448827d544c6a4ea6052730a23774e43d805675dc4eff1e62dac7430fbb ./vendor/libchrome/base/threading/thread_local.h +4ce66ab1de62adcd404711aed89d5ce6866930cf0903275b32a67d83c78e5dfc ./vendor/libchrome/base/threading/thread_local_storage.cc +9ec1a85d04bdf1e460791ec8825f0af1e25d4a9962069dbf90b55089a41b4408 ./vendor/libchrome/base/threading/thread_local_storage.h +294837f7209f5398f09fc050a4dc7737fe7a35f48c5fedb7be49102a36ceb7c8 ./vendor/libchrome/base/threading/thread_restrictions.h +20a42edaec24cb2b33d7134c1efb0b9343a0dcf4c825f676cefee6e09be7454d ./vendor/libchrome/base/threading/thread_task_runner_handle.h +f3045ca162b851e605cf21dd3b5369f74d5d937c41bab06770b7d45369cf8833 ./vendor/libchrome/base/time/time.cc +fd4782801bfdf9286362116a6a7a905ccfbc55df32645f1967d8cbc265f0e6a8 ./vendor/libchrome/base/time/time.h +6817b3e18ea1f6dc3c67195380e5b873cd6952acbf7600edd54df1526623d997 ./vendor/libchrome/base/time/time_now_posix.cc +5fd3201d87e6d2e314f6397ad2e81b7a7d0100df233af3f2a488c4480e096640 ./vendor/libchrome/base/time/time_override.h +f69f40e3b8536c7df01132d4d4028fc0c25ec0d4223c965536564691e50344fe ./vendor/libchrome/base/token.h +bc23ab463856a5e56ab83b1ef3c4226099bbb7bf6c427c32ae15d42a21f6bfed ./vendor/libchrome/base/unguessable_token.h +c23322de7ef5b172228c9009237d15d2faf7106e30e0278f3d4519d78bfc4cd3 ./vendor/libchrome/base/vlog.h +12b6cd8acf682fe2a6d90c469619c4b9d794d7fe9c3172d9d5e024d5a1659bfd ./vendor/libchrome/build/build_config.h +3039541e5c5d018230b8bee6a5e59eb4d6bafa7c68732b9e61b41fd81a27bc3f ./vendor/libchrome/testing/gtest/include/gtest/gtest_prod.h +845022e0c1db1abb41a6ba4cd3c4b674ec290f3359d9d3c78ae558d4c0ed9308 ./vendor/zucchini/LICENSE +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ./vendor/zucchini/MODULE_LICENSE_CHROME +4701a0af1d9be83a5b24d65490c75c2c37772333b38ad3fc7878e8fc9d8267e0 ./vendor/zucchini/abs32_utils.cc +859d2681911be3697121b7eea7b5382b28da16deb8b068a03ef6e0c361dbd445 ./vendor/zucchini/address_translator.cc +ab9c9deb93f16a550ac6a34907b78c272f2a19604e5409473895315e29bcb2c3 ./vendor/zucchini/aosp/include/base/check.h +05525647e279146b36863904530dd0006d1f82f99fd2767ff39d69239e235aea ./vendor/zucchini/aosp/include/base/check_op.h +43d9d019e637b750a7bc1e0a3944d1d4cea12cdff4e0905474e16db58415b6a2 ./vendor/zucchini/aosp/include/base/containers/cxx20_erase.h +30ef02104a03e0d55e3bc63c048f9057312f0131b2b949397e1d978dd011695a ./vendor/zucchini/aosp/include/build/buildflag.h +9df450f653d9e0026b162f57a2c77133836cd62df378e25534322b6ee3e98ab5 ./vendor/zucchini/aosp/include/components/zucchini/abs32_utils.h +3ed0c865d024dcce6538903397ce6ac72c40c2a3ba0f16bc4a92f1180924269d ./vendor/zucchini/aosp/include/components/zucchini/address_translator.h +4ab4a0705c8376ae4619d6122afd390118d94ad90e7909da2e532884423f1d2e ./vendor/zucchini/aosp/include/components/zucchini/algorithm.h +3f069245e00549328cbf36cfb78dcc9b2ab90c08a5476d82c49f6c7fa165b8a7 ./vendor/zucchini/aosp/include/components/zucchini/arm_utils.h +f149c8b80c4234ad7be88d896e4498524f48f168cb1c31637e9c1d1695b2cf8e ./vendor/zucchini/aosp/include/components/zucchini/buffer_sink.h +c25889b6dd08e241d8903f460284a2792e73ad88776c787f41b1ac480b51fb0a ./vendor/zucchini/aosp/include/components/zucchini/buffer_source.h +5bd8910752183f45f840b6065dd94e5bc9e7481f83fb7631c4ef25df687b854d ./vendor/zucchini/aosp/include/components/zucchini/buffer_view.h +f739b6c029ec40a9f7bd5305c3d0ae5e428c085797fa18739fddd46a0fb98663 ./vendor/zucchini/aosp/include/components/zucchini/crc32.h +03780b011ee7c187fce41b42aee32de5ce1078c76c32366fb16b4c9aaf6e05db ./vendor/zucchini/aosp/include/components/zucchini/disassembler.h +b9c91a0dd897bfaa26df6c3247aaf99e75cd264357f615bf2e109b9f7b119080 ./vendor/zucchini/aosp/include/components/zucchini/disassembler_dex.h +852e3797431f194424044d210da3f53cdeceefb23e8baa51a21ec73b38dd5733 ./vendor/zucchini/aosp/include/components/zucchini/disassembler_elf.h +69bc539bf723efccb937c5fe93d29f49f6639559920d9259ee44a4f46e134c19 ./vendor/zucchini/aosp/include/components/zucchini/disassembler_no_op.h +b5e86cf5987049adb790b81a485b108f2a5db180fcef9234a7b5948159cf5ce0 ./vendor/zucchini/aosp/include/components/zucchini/element_detection.h +ccd17f233302411ba5d2df59071ab7201893422aa9d54db54b2d75dddbad22ba ./vendor/zucchini/aosp/include/components/zucchini/encoded_view.h +952c790d808deb6bb9c5bccd00a8e6504ecf474ade5312303e078b1de3fb5065 ./vendor/zucchini/aosp/include/components/zucchini/equivalence_map.h +7c6f41c221036f83051e057eeffcbcd509a556ca994bba85b0b78c6a6cb5dcc4 ./vendor/zucchini/aosp/include/components/zucchini/image_index.h +908655de17bfebe5eaeeade4cdd2a567a2907e5e013cb0bb19d820a92aa8ec96 ./vendor/zucchini/aosp/include/components/zucchini/image_utils.h +a3bc23d7687bcf4e5d316b7e8e7771797be973dc9bde2977bab80ef946665cc0 ./vendor/zucchini/aosp/include/components/zucchini/io_utils.h +d28d66e9de9ba132d6f9b907e18ae7c61801a4fe8737a2a5e5206b5906315e19 ./vendor/zucchini/aosp/include/components/zucchini/patch_reader.h +53f48d894382fec2677ac434e139b770bcad69a8f642a945f1ccfe44938be211 ./vendor/zucchini/aosp/include/components/zucchini/patch_utils.h +d6556ba0cf992253dbfa0e469927b3238279865ebb8b9795752b08ad55adaff5 ./vendor/zucchini/aosp/include/components/zucchini/patch_writer.h +6562d611fab6975c17fbf375d9654878c5f6ea9c711394d5f54fa6a2b34ba7a7 ./vendor/zucchini/aosp/include/components/zucchini/reference_set.h +e461b4da6a293817f7aba5005f8d53b3597e931899ab5b4a0f725c830cf442b3 ./vendor/zucchini/aosp/include/components/zucchini/rel32_finder.h +92214559985d7813f39651fc56820c9aad03cd360398cd778fd84880f6abe08d ./vendor/zucchini/aosp/include/components/zucchini/rel32_utils.h +85deddab8aec5f2eb2c7d38e35900434ab0bdb98ca82d03be1114920c229ef12 ./vendor/zucchini/aosp/include/components/zucchini/reloc_elf.h +608a7b2e6a6c998d9641d46f0b138e44c95acf2559ddfc31fdf52fe61806d7d6 ./vendor/zucchini/aosp/include/components/zucchini/suffix_array.h +e7a1365b362bda44cf68df75f21d3c6df49f6c75e04a53788d3d3d3451cab935 ./vendor/zucchini/aosp/include/components/zucchini/target_pool.h +8dc3dab58827d8c1432eafa7714a1d39e7e9e2d132b895b9997f4226b9937a4a ./vendor/zucchini/aosp/include/components/zucchini/targets_affinity.h +f4e5ad0c852f80f2929e95fb81ff095abb3f2c9ecc9257254558bf49cc99d453 ./vendor/zucchini/aosp/include/components/zucchini/type_dex.h +9204b7970c7b1f3ec27f5f3970fc5131c5d4d3177052a93f47a9ab378e16c958 ./vendor/zucchini/aosp/include/components/zucchini/type_elf.h +74733f75da09ff5e3a3d7e15c662eac946f6acd58033579673c3ddfc6952692f ./vendor/zucchini/aosp/include/components/zucchini/typed_value.h +480438e1cde53fecf15fba573aaf3c60c0a0870c6cc53efdd6746d6ad5b00a43 ./vendor/zucchini/aosp/include/components/zucchini/version_info.h +f78d8351a1483bdb35774eb37b4f68f64eeca722eaf433437d1cdb32b5db0a5b ./vendor/zucchini/aosp/include/components/zucchini/zucchini.h +9033c748e797f9db69ee599d994d51763fd531cd2bf1793a635d52ce22994972 ./vendor/zucchini/aosp/include/components/zucchini/zucchini_apply.h +027123695382e7a5630d5da6841fe87d9b229b8bfb2e5cca6467da266ae95c9a ./vendor/zucchini/arm_utils.cc +ab75e4e8d05002d9db732fb92d3d36ac862a823d92d8be14ef595f452dc746eb ./vendor/zucchini/buffer_source.cc +9994adabd554adf6992231d74ca4dec66651e86b3987017f432cb04d04d8d48e ./vendor/zucchini/crc32.cc +3403dcba9034f039cfe245fe143b6e73e1ea7515ca3fd307eb8ee4be6cbf8128 ./vendor/zucchini/disassembler.cc +02948825abc47bffaa1dd1c774b06394fe7192b4e74e33f14d4a61d263989f7e ./vendor/zucchini/disassembler_dex.cc +5b853a907c7bb883d7485cb22ec7d8def7c6df432ddfd54c49f96c8cf558276a ./vendor/zucchini/disassembler_elf.cc +4054b3a647ef5d16e3006526db1b34a39b8de8ba49871c1a88ea4778b0599d9d ./vendor/zucchini/disassembler_no_op.cc +e21f53a5d599a256f97d54abe890e21722847fe7aa2373b4a5968b74976e2ae8 ./vendor/zucchini/element_detection.cc +5dc41a3c41970077b99dc5c6028cf9c5fa74b3fd9d115a2936f0064814761fb5 ./vendor/zucchini/equivalence_map.cc +b0d6670916d0d5f6f0668605551d6fed1701231986f4e40538394dab7dd1660c ./vendor/zucchini/patch_reader.cc +175c78a1b4b9026c78fb891b5419ad3a90db7d75d0a39147e11f11d400fedd34 ./vendor/zucchini/rel32_finder.cc +74e3c8f99128448b0fe75d0daf8efc31748491d1a3ea523d0a129c743d37440f ./vendor/zucchini/rel32_utils.cc +e472b81aebe0a511112dc88da24a70c65abfcbbc97c4cbe14b20372d690dec4b ./vendor/zucchini/reloc_elf.cc +a687290bda9b85b6696a9e983cb39c7e1477bc0da92ce46734ebd00302cae85f ./vendor/zucchini/target_pool.cc +e301b504ee2df95fbca1be829cd7f47384e07145b6316df14e370da5e9c7647f ./vendor/zucchini/zucchini_apply.cc diff --git a/native/zucchini/include/components/zucchini/buildflags.h b/native/zucchini/include/components/zucchini/buildflags.h new file mode 100644 index 0000000..fbd2c41 --- /dev/null +++ b/native/zucchini/include/components/zucchini/buildflags.h @@ -0,0 +1,12 @@ +// Proof-only copy of the pinned Android generated buildflags.h. +#ifndef COMPONENTS_ZUCCHINI_BUILDFLAGS_H_ +#define COMPONENTS_ZUCCHINI_BUILDFLAGS_H_ + +#include "build/buildflag.h" + +#define BUILDFLAG_INTERNAL_ENABLE_DEX() (1) +#define BUILDFLAG_INTERNAL_ENABLE_ELF() (1) +#define BUILDFLAG_INTERNAL_ENABLE_WIN() (0) +#define BUILDFLAG_INTERNAL_ENABLE_ZTF() (0) + +#endif // COMPONENTS_ZUCCHINI_BUILDFLAGS_H_ diff --git a/native/zucchini/include/gtest/gtest_prod.h b/native/zucchini/include/gtest/gtest_prod.h new file mode 100644 index 0000000..125a46c --- /dev/null +++ b/native/zucchini/include/gtest/gtest_prod.h @@ -0,0 +1,5 @@ +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_ +#define FRIEND_TEST(test_case_name, test_name) \ + friend class test_case_name##_##test_name##_Test +#endif diff --git a/native/zucchini/src/zucchini_ffi.cc b/native/zucchini/src/zucchini_ffi.cc new file mode 100644 index 0000000..d2faf47 --- /dev/null +++ b/native/zucchini/src/zucchini_ffi.cc @@ -0,0 +1,353 @@ +#include "zucchini_ffi.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "components/zucchini/disassembler.h" +#include "components/zucchini/disassembler_dex.h" +#include "components/zucchini/element_detection.h" +#include "components/zucchini/equivalence_map.h" +#include "components/zucchini/image_utils.h" +#include "components/zucchini/patch_reader.h" +#include "components/zucchini/target_pool.h" +#include "components/zucchini/type_dex.h" +#include "components/zucchini/type_elf.h" +#include "components/zucchini/zucchini_apply.h" + +namespace { + +otadump_zucchini_result Error(otadump_zucchini_status status, const char* error) { + return {status, error}; +} + +bool RangeIsRepresentable(const uint8_t* data, size_t size) { + uintptr_t begin = reinterpret_cast(data); + return size <= std::numeric_limits::max() - begin; +} + +bool RangesOverlap(const uint8_t* left, + size_t left_size, + const uint8_t* right, + size_t right_size) { + if (!left_size || !right_size) + return false; + uintptr_t left_begin = reinterpret_cast(left); + uintptr_t right_begin = reinterpret_cast(right); + return left_begin < right_begin + right_size && + right_begin < left_begin + left_size; +} + +bool IsAndroidExecutable(zucchini::ExecutableType exe_type) { + return exe_type == zucchini::kExeTypeDex || + exe_type == zucchini::kExeTypeElfX86 || + exe_type == zucchini::kExeTypeElfX64 || + exe_type == zucchini::kExeTypeElfAArch32 || + exe_type == zucchini::kExeTypeElfAArch64; +} + +bool ReadDexMapItem(zucchini::ConstBufferView image, + uint16_t type, + zucchini::dex::MapItem* result) { + using Header = zucchini::dex::HeaderItem; + using MapItem = zucchini::dex::MapItem; + if (!image.can_access
(0)) + return false; + Header header; + std::memcpy(&header, image.begin(), sizeof(header)); + if (!image.can_access(header.map_off)) + return false; + uint32_t count = 0; + std::memcpy(&count, image.begin() + header.map_off, sizeof(count)); + size_t items_offset = header.map_off + sizeof(count); + if (!image.covers_array(items_offset, count, sizeof(MapItem))) + return false; + for (uint32_t index = 0; index < count; ++index) { + MapItem item; + std::memcpy(&item, image.begin() + items_offset + index * sizeof(item), + sizeof(item)); + if (item.type == type) { + *result = item; + return true; + } + } + return false; +} + +bool ValidateDexWriterWidths(zucchini::ConstBufferView image) { + const uint16_t narrow_types[] = { + zucchini::dex::kTypeTypeIdItem, + zucchini::dex::kTypeProtoIdItem, + zucchini::dex::kTypeFieldIdItem, + zucchini::dex::kTypeMethodIdItem, + zucchini::dex::kTypeCallSiteIdItem, + zucchini::dex::kTypeMethodHandleItem, + }; + for (uint16_t type : narrow_types) { + zucchini::dex::MapItem item; + if (ReadDexMapItem(image, type, &item) && + item.size > static_cast(UINT16_MAX) + 1) { + return false; + } + } + return true; +} + +bool ValidateDexReferenceTargets( + const zucchini::PatchElementReader& element, + zucchini::Disassembler* old_disasm, + zucchini::Disassembler* new_disasm, + size_t old_size, + size_t new_size, + zucchini::ConstBufferView new_image) { + zucchini::dex::MapItem string_ids; + if (!ReadDexMapItem(new_image, zucchini::dex::kTypeStringIdItem, + &string_ids)) { + return false; + } + + zucchini::ReferenceDeltaSource deltas = element.GetReferenceDeltaSource(); + std::map> pools; + for (const auto& group : old_disasm->MakeReferenceGroups()) + pools[group.pool_tag()].push_back(group); + std::vector new_groups = + new_disasm->MakeReferenceGroups(); + zucchini::OffsetMapper mapper( + element.GetEquivalenceSource(), + base::checked_cast(old_size), + base::checked_cast(new_size)); + + for (const auto& pool : pools) { + zucchini::TargetPool targets; + for (zucchini::ReferenceGroup group : pool.second) + targets.InsertTargets(std::move(*group.GetReader(old_disasm))); + targets.FilterAndProject(mapper); + + zucchini::TargetSource extras = element.GetExtraTargetSource(pool.first); + targets.InsertTargets(&extras); + if (!extras.Done()) + return false; + + for (const zucchini::ReferenceGroup& group : pool.second) { + if (group.type_tag().value() >= new_groups.size()) + return false; + zucchini::EquivalenceSource equivalences = + element.GetEquivalenceSource(); + for (auto equivalence = equivalences.GetNext(); equivalence.has_value(); + equivalence = equivalences.GetNext()) { + std::unique_ptr references = + group.GetReader(equivalence->src_offset, equivalence->src_end(), + old_disasm); + for (auto reference = references->GetNext(); reference.has_value(); + reference = references->GetNext()) { + zucchini::offset_t projected = + mapper.ExtendedForwardProject(reference->target); + zucchini::key_t expected = targets.KeyForNearestOffset(projected); + std::optional delta = deltas.GetNext(); + if (!delta.has_value()) + return false; + int64_t key = static_cast(expected) + delta.value(); + if (key < 0 || key > UINT32_MAX || + !targets.KeyIsValid(static_cast(key))) { + return false; + } + if (group.type_tag().value() == + zucchini::DisassemblerDex::kCodeToStringId16) { + zucchini::offset_t target = + targets.OffsetForKey(static_cast(key)); + if (target < string_ids.offset || + (target - string_ids.offset) % + sizeof(zucchini::dex::StringIdItem) != + 0 || + (target - string_ids.offset) / + sizeof(zucchini::dex::StringIdItem) > + UINT16_MAX) { + return false; + } + } + } + } + } + } + return deltas.Done(); +} + +bool ValidateReferenceBoundaries( + const zucchini::PatchElementReader& element, + zucchini::Disassembler* old_disasm, + zucchini::ExecutableType exe_type, + size_t new_size) { + std::vector boundaries; + std::vector parsed_equivalences; + zucchini::EquivalenceSource equivalences = element.GetEquivalenceSource(); + for (auto equivalence = equivalences.GetNext(); equivalence.has_value(); + equivalence = equivalences.GetNext()) { + boundaries.push_back(equivalence->src_offset); + boundaries.push_back(equivalence->src_end()); + parsed_equivalences.push_back(*equivalence); + } + std::sort(boundaries.begin(), boundaries.end()); + + for (const auto& group : old_disasm->MakeReferenceGroups()) { + size_t writer_width = group.width(); + if (group.type_tag().value() == 0) { + if (exe_type == zucchini::kExeTypeElfX86 || + exe_type == zucchini::kExeTypeElfAArch32) { + writer_width = sizeof(zucchini::elf::Elf32_Rel); + } else if (exe_type == zucchini::kExeTypeElfX64 || + exe_type == zucchini::kExeTypeElfAArch64) { + writer_width = sizeof(zucchini::elf::Elf64_Rel); + } + } + + std::unique_ptr reader = + group.GetReader(old_disasm); + for (auto reference = reader->GetNext(); reference.has_value(); + reference = reader->GetNext()) { + auto boundary = + std::upper_bound(boundaries.begin(), boundaries.end(), + reference->location); + if (boundary != boundaries.end() && + *boundary < reference->location + group.width()) { + return false; + } + } + + // Upstream creates a fresh ranged reader for each equivalence. Mirror + // those reads so an invalid target before a later equivalence cannot hide + // a reference whose source or projected writer body crosses a boundary. + for (const auto& equivalence : parsed_equivalences) { + std::unique_ptr ranged_reader = + group.GetReader(equivalence.src_offset, equivalence.src_end(), + old_disasm); + for (auto reference = ranged_reader->GetNext(); reference.has_value(); + reference = ranged_reader->GetNext()) { + if (reference->location < equivalence.src_offset || + reference->location > equivalence.src_end() || + group.width() > equivalence.src_end() - reference->location) { + return false; + } + size_t projected = equivalence.dst_offset + + reference->location - equivalence.src_offset; + if (projected > new_size || writer_width > new_size - projected) + return false; + } + } + } + return true; +} + +bool PreflightAndroidElements( + zucchini::ConstBufferView old_image, + const zucchini::EnsemblePatchReader& patch, + zucchini::MutableBufferView new_image) { + if (!patch.CheckOldFile(old_image)) + return false; + for (const auto& element : patch.elements()) { + zucchini::ExecutableType exe_type = element.element_match().exe_type(); + if (!IsAndroidExecutable(exe_type)) + continue; + zucchini::ConstBufferView old_element = old_image[element.old_element()]; + zucchini::MutableBufferView new_element = new_image[element.new_element()]; + if (!zucchini::ApplyEquivalenceAndExtraData(old_element, element, + new_element) || + !zucchini::ApplyRawDelta(element, new_element)) { + return false; + } + auto old_disasm = zucchini::MakeDisassemblerOfType(old_element, exe_type); + auto new_disasm = zucchini::MakeDisassemblerOfType( + zucchini::ConstBufferView(new_element), exe_type); + if (!old_disasm || !new_disasm || + old_disasm->size() != old_element.size() || + new_disasm->size() != new_element.size() || + !ValidateReferenceBoundaries(element, old_disasm.get(), exe_type, + new_element.size())) { + return false; + } + if (exe_type == zucchini::kExeTypeDex) { + if (!ValidateDexWriterWidths(zucchini::ConstBufferView(new_element)) || + !ValidateDexReferenceTargets( + element, old_disasm.get(), new_disasm.get(), old_element.size(), + new_element.size(), zucchini::ConstBufferView(new_element))) { + return false; + } + } + } + return true; +} + +} // namespace + +extern "C" otadump_zucchini_result otadump_zucchini_apply( + const uint8_t* old_data, + size_t old_size, + const uint8_t* patch_data, + size_t patch_size, + uint8_t* new_data, + size_t new_size) { + if ((!old_data && old_size) || (!patch_data && patch_size) || + (!new_data && new_size)) { + return Error(OTADUMP_ZUCCHINI_INVALID_ARGUMENT, "null non-empty buffer"); + } + if (!RangeIsRepresentable(old_data, old_size) || + !RangeIsRepresentable(patch_data, patch_size) || + !RangeIsRepresentable(new_data, new_size)) { + return Error(OTADUMP_ZUCCHINI_INVALID_ARGUMENT, "buffer range wraps"); + } + if (RangesOverlap(new_data, new_size, old_data, old_size) || + RangesOverlap(new_data, new_size, patch_data, patch_size)) { + return Error(OTADUMP_ZUCCHINI_INVALID_ARGUMENT, + "output overlaps an input buffer"); + } + if (old_size >= zucchini::kOffsetBound || new_size >= zucchini::kOffsetBound) { + return Error(OTADUMP_ZUCCHINI_INVALID_ARGUMENT, + "image exceeds Zucchini offset bound"); + } + + // BufferView performs pointer arithmetic in its constructor. Normalize + // empty null buffers to a valid one-past-capable address. + static const uint8_t empty_input = 0; + static uint8_t empty_output = 0; + old_data = old_data ? old_data : &empty_input; + patch_data = patch_data ? patch_data : &empty_input; + new_data = new_data ? new_data : &empty_output; + + auto patch = zucchini::EnsemblePatchReader::Create( + zucchini::ConstBufferView(patch_data, patch_size)); + if (!patch) { + return Error(OTADUMP_ZUCCHINI_INVALID_PATCH, "invalid ensemble patch"); + } + if (patch->header().new_size != new_size) { + return Error(OTADUMP_ZUCCHINI_WRONG_OUTPUT_SIZE, + "output buffer size does not match patch"); + } + + for (const auto& element : patch->elements()) { + const zucchini::ExecutableType exe_type = + element.element_match().exe_type(); + if (exe_type != zucchini::kExeTypeNoOp && !IsAndroidExecutable(exe_type)) { + return Error(OTADUMP_ZUCCHINI_UNSUPPORTED_ELEMENT, + "element format is not enabled by Android"); + } + } + + if (!PreflightAndroidElements( + zucchini::ConstBufferView(old_data, old_size), *patch, + zucchini::MutableBufferView(new_data, new_size))) { + return Error(OTADUMP_ZUCCHINI_APPLY_ERROR, + "android executable preflight failed"); + } + + zucchini::status::Code status = zucchini::ApplyBuffer( + zucchini::ConstBufferView(old_data, old_size), *patch, + zucchini::MutableBufferView(new_data, new_size)); + if (status != zucchini::status::kStatusSuccess) { + return Error(OTADUMP_ZUCCHINI_APPLY_ERROR, "zucchini apply failed"); + } + return {OTADUMP_ZUCCHINI_OK, nullptr}; +} diff --git a/native/zucchini/src/zucchini_ffi.h b/native/zucchini/src/zucchini_ffi.h new file mode 100644 index 0000000..18ec992 --- /dev/null +++ b/native/zucchini/src/zucchini_ffi.h @@ -0,0 +1,39 @@ +#ifndef OTADUMP_ZUCCHINI_FFI_H_ +#define OTADUMP_ZUCCHINI_FFI_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum otadump_zucchini_status { + OTADUMP_ZUCCHINI_OK = 0, + OTADUMP_ZUCCHINI_INVALID_ARGUMENT = 1, + OTADUMP_ZUCCHINI_INVALID_PATCH = 2, + OTADUMP_ZUCCHINI_UNSUPPORTED_ELEMENT = 3, + OTADUMP_ZUCCHINI_WRONG_OUTPUT_SIZE = 4, + OTADUMP_ZUCCHINI_APPLY_ERROR = 5, +}; + +struct otadump_zucchini_result { + int status; + const char* error; +}; + +// All non-empty buffers must remain valid for the complete call. Input buffers +// are read-only. The output buffer must not overlap either input buffer. +struct otadump_zucchini_result otadump_zucchini_apply( + const uint8_t* old_data, + size_t old_size, + const uint8_t* patch_data, + size_t patch_size, + uint8_t* new_data, + size_t new_size); + +#ifdef __cplusplus +} +#endif + +#endif // OTADUMP_ZUCCHINI_FFI_H_ diff --git a/native/zucchini/vendor/libchrome/MODULE_LICENSE_BSD b/native/zucchini/vendor/libchrome/MODULE_LICENSE_BSD new file mode 100644 index 0000000..e69de29 diff --git a/native/zucchini/vendor/libchrome/NOTICE b/native/zucchini/vendor/libchrome/NOTICE new file mode 100644 index 0000000..972bb2e --- /dev/null +++ b/native/zucchini/vendor/libchrome/NOTICE @@ -0,0 +1,27 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/native/zucchini/vendor/libchrome/base/at_exit.cc b/native/zucchini/vendor/libchrome/base/at_exit.cc new file mode 100644 index 0000000..52c2151 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/at_exit.cc @@ -0,0 +1,109 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/at_exit.h" + +#include +#include +#include + +#include "base/bind.h" +#include "base/callback.h" +#include "base/logging.h" + +namespace base { + +// Keep a stack of registered AtExitManagers. We always operate on the most +// recent, and we should never have more than one outside of testing (for a +// statically linked version of this library). Testing may use the shadow +// version of the constructor, and if we are building a dynamic library we may +// end up with multiple AtExitManagers on the same process. We don't protect +// this for thread-safe access, since it will only be modified in testing. +static AtExitManager* g_top_manager = nullptr; + +static bool g_disable_managers = false; + +AtExitManager::AtExitManager() + : processing_callbacks_(false), next_manager_(g_top_manager) { +// If multiple modules instantiate AtExitManagers they'll end up living in this +// module... they have to coexist. +#if !defined(COMPONENT_BUILD) + DCHECK(!g_top_manager); +#endif + g_top_manager = this; +} + +AtExitManager::~AtExitManager() { + if (!g_top_manager) { + NOTREACHED() << "Tried to ~AtExitManager without an AtExitManager"; + return; + } + DCHECK_EQ(this, g_top_manager); + + if (!g_disable_managers) + ProcessCallbacksNow(); + g_top_manager = next_manager_; +} + +// static +void AtExitManager::RegisterCallback(AtExitCallbackType func, void* param) { + DCHECK(func); + RegisterTask(base::Bind(func, param)); +} + +// static +void AtExitManager::RegisterTask(base::Closure task) { + if (!g_top_manager) { + NOTREACHED() << "Tried to RegisterCallback without an AtExitManager"; + return; + } + + AutoLock lock(g_top_manager->lock_); + DCHECK(!g_top_manager->processing_callbacks_); + g_top_manager->stack_.push(std::move(task)); +} + +// static +void AtExitManager::ProcessCallbacksNow() { + if (!g_top_manager) { + NOTREACHED() << "Tried to ProcessCallbacksNow without an AtExitManager"; + return; + } + + // Callbacks may try to add new callbacks, so run them without holding + // |lock_|. This is an error and caught by the DCHECK in RegisterTask(), but + // handle it gracefully in release builds so we don't deadlock. + base::stack tasks; + { + AutoLock lock(g_top_manager->lock_); + tasks.swap(g_top_manager->stack_); + g_top_manager->processing_callbacks_ = true; + } + + // Relax the cross-thread access restriction to non-thread-safe RefCount. + // It's safe since all other threads should be terminated at this point. + ScopedAllowCrossThreadRefCountAccess allow_cross_thread_ref_count_access; + + while (!tasks.empty()) { + base::Closure task = tasks.top(); + task.Run(); + tasks.pop(); + } + + // Expect that all callbacks have been run. + DCHECK(g_top_manager->stack_.empty()); +} + +void AtExitManager::DisableAllAtExitManagers() { + AutoLock lock(g_top_manager->lock_); + g_disable_managers = true; +} + +AtExitManager::AtExitManager(bool shadow) + : processing_callbacks_(false), next_manager_(g_top_manager) { + DCHECK(shadow || !g_top_manager); + g_top_manager = this; +} + +} // namespace base diff --git a/native/zucchini/vendor/libchrome/base/at_exit.h b/native/zucchini/vendor/libchrome/base/at_exit.h new file mode 100644 index 0000000..e74de8d --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/at_exit.h @@ -0,0 +1,80 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_AT_EXIT_H_ +#define BASE_AT_EXIT_H_ + +#include "base/base_export.h" +#include "base/callback.h" +#include "base/containers/stack.h" +#include "base/macros.h" +#include "base/synchronization/lock.h" + +namespace base { + +// This class provides a facility similar to the CRT atexit(), except that +// we control when the callbacks are executed. Under Windows for a DLL they +// happen at a really bad time and under the loader lock. This facility is +// mostly used by base::Singleton. +// +// The usage is simple. Early in the main() or WinMain() scope create an +// AtExitManager object on the stack: +// int main(...) { +// base::AtExitManager exit_manager; +// +// } +// When the exit_manager object goes out of scope, all the registered +// callbacks and singleton destructors will be called. + +class BASE_EXPORT AtExitManager { + public: + typedef void (*AtExitCallbackType)(void*); + + AtExitManager(); + + // The dtor calls all the registered callbacks. Do not try to register more + // callbacks after this point. + ~AtExitManager(); + + // Registers the specified function to be called at exit. The prototype of + // the callback function is void func(void*). + static void RegisterCallback(AtExitCallbackType func, void* param); + + // Registers the specified task to be called at exit. + static void RegisterTask(base::Closure task); + + // Calls the functions registered with RegisterCallback in LIFO order. It + // is possible to register new callbacks after calling this function. + static void ProcessCallbacksNow(); + + // Disable all registered at-exit callbacks. This is used only in a single- + // process mode. + static void DisableAllAtExitManagers(); + + protected: + // This constructor will allow this instance of AtExitManager to be created + // even if one already exists. This should only be used for testing! + // AtExitManagers are kept on a global stack, and it will be removed during + // destruction. This allows you to shadow another AtExitManager. + explicit AtExitManager(bool shadow); + + private: + base::Lock lock_; + base::stack stack_; + bool processing_callbacks_; + AtExitManager* next_manager_; // Stack of managers to allow shadowing. + + DISALLOW_COPY_AND_ASSIGN(AtExitManager); +}; + +#if defined(UNIT_TEST) +class ShadowingAtExitManager : public AtExitManager { + public: + ShadowingAtExitManager() : AtExitManager(true) {} +}; +#endif // defined(UNIT_TEST) + +} // namespace base + +#endif // BASE_AT_EXIT_H_ diff --git a/native/zucchini/vendor/libchrome/base/atomic_ref_count.h b/native/zucchini/vendor/libchrome/base/atomic_ref_count.h new file mode 100644 index 0000000..3ffa017 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/atomic_ref_count.h @@ -0,0 +1,67 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This is a low level implementation of atomic semantics for reference +// counting. Please use base/memory/ref_counted.h directly instead. + +#ifndef BASE_ATOMIC_REF_COUNT_H_ +#define BASE_ATOMIC_REF_COUNT_H_ + +#include + +namespace base { + +class AtomicRefCount { + public: + constexpr AtomicRefCount() : ref_count_(0) {} + explicit constexpr AtomicRefCount(int initial_value) + : ref_count_(initial_value) {} + + // Increment a reference count. + void Increment() { Increment(1); } + + // Increment a reference count by "increment", which must exceed 0. + void Increment(int increment) { + ref_count_.fetch_add(increment, std::memory_order_relaxed); + } + + // Decrement a reference count, and return whether the result is non-zero. + // Insert barriers to ensure that state written before the reference count + // became zero will be visible to a thread that has just made the count zero. + bool Decrement() { + // TODO(jbroman): Technically this doesn't need to be an acquire operation + // unless the result is 1 (i.e., the ref count did indeed reach zero). + // However, there are toolchain issues that make that not work as well at + // present (notably TSAN doesn't like it). + return ref_count_.fetch_sub(1, std::memory_order_acq_rel) != 1; + } + + // Return whether the reference count is one. If the reference count is used + // in the conventional way, a refrerence count of 1 implies that the current + // thread owns the reference and no other thread shares it. This call + // performs the test for a reference count of one, and performs the memory + // barrier needed for the owning thread to act on the object, knowing that it + // has exclusive access to the object. + bool IsOne() const { return ref_count_.load(std::memory_order_acquire) == 1; } + + // Return whether the reference count is zero. With conventional object + // referencing counting, the object will be destroyed, so the reference count + // should never be zero. Hence this is generally used for a debug check. + bool IsZero() const { + return ref_count_.load(std::memory_order_acquire) == 0; + } + + // Returns the current reference count (with no barriers). This is subtle, and + // should be used only for debugging. + int SubtleRefCountForDebug() const { + return ref_count_.load(std::memory_order_relaxed); + } + + private: + std::atomic_int ref_count_; +}; + +} // namespace base + +#endif // BASE_ATOMIC_REF_COUNT_H_ diff --git a/native/zucchini/vendor/libchrome/base/atomic_sequence_num.h b/native/zucchini/vendor/libchrome/base/atomic_sequence_num.h new file mode 100644 index 0000000..717e37a --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/atomic_sequence_num.h @@ -0,0 +1,33 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_ATOMIC_SEQUENCE_NUM_H_ +#define BASE_ATOMIC_SEQUENCE_NUM_H_ + +#include + +#include "base/macros.h" + +namespace base { + +// AtomicSequenceNumber is a thread safe increasing sequence number generator. +// Its constructor doesn't emit a static initializer, so it's safe to use as a +// global variable or static member. +class AtomicSequenceNumber { + public: + constexpr AtomicSequenceNumber() = default; + + // Returns an increasing sequence number starts from 0 for each call. + // This function can be called from any thread without data race. + inline int GetNext() { return seq_.fetch_add(1, std::memory_order_relaxed); } + + private: + std::atomic_int seq_{0}; + + DISALLOW_COPY_AND_ASSIGN(AtomicSequenceNumber); +}; + +} // namespace base + +#endif // BASE_ATOMIC_SEQUENCE_NUM_H_ diff --git a/native/zucchini/vendor/libchrome/base/atomicops.h b/native/zucchini/vendor/libchrome/base/atomicops.h new file mode 100644 index 0000000..4d8510e --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/atomicops.h @@ -0,0 +1,161 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// For atomic operations on reference counts, see atomic_refcount.h. +// For atomic operations on sequence numbers, see atomic_sequence_num.h. + +// The routines exported by this module are subtle. If you use them, even if +// you get the code right, it will depend on careful reasoning about atomicity +// and memory ordering; it will be less readable, and harder to maintain. If +// you plan to use these routines, you should have a good reason, such as solid +// evidence that performance would otherwise suffer, or there being no +// alternative. You should assume only properties explicitly guaranteed by the +// specifications in this file. You are almost certainly _not_ writing code +// just for the x86; if you assume x86 semantics, x86 hardware bugs and +// implementations on other archtectures will cause your code to break. If you +// do not know what you are doing, avoid these routines, and use a Mutex. +// +// It is incorrect to make direct assignments to/from an atomic variable. +// You should use one of the Load or Store routines. The NoBarrier +// versions are provided when no barriers are needed: +// NoBarrier_Store() +// NoBarrier_Load() +// Although there are currently no compiler enforcement, you are encouraged +// to use these. +// + +#ifndef BASE_ATOMICOPS_H_ +#define BASE_ATOMICOPS_H_ + +#include + +// Small C++ header which defines implementation specific macros used to +// identify the STL implementation. +// - libc++: captures __config for _LIBCPP_VERSION +// - libstdc++: captures bits/c++config.h for __GLIBCXX__ +#include + +#include "base/base_export.h" +#include "build/build_config.h" + +#if defined(OS_WIN) && defined(ARCH_CPU_64_BITS) +// windows.h #defines this (only on x64). This causes problems because the +// public API also uses MemoryBarrier at the public name for this fence. So, on +// X64, undef it, and call its documented +// (http://msdn.microsoft.com/en-us/library/windows/desktop/ms684208.aspx) +// implementation directly. +#undef MemoryBarrier +#endif + +namespace base { +namespace subtle { + +typedef int32_t Atomic32; +#ifdef ARCH_CPU_64_BITS +// We need to be able to go between Atomic64 and AtomicWord implicitly. This +// means Atomic64 and AtomicWord should be the same type on 64-bit. +#if defined(__ILP32__) || defined(OS_NACL) +// NaCl's intptr_t is not actually 64-bits on 64-bit! +// http://code.google.com/p/nativeclient/issues/detail?id=1162 +typedef int64_t Atomic64; +#else +typedef intptr_t Atomic64; +#endif +#endif + +// Use AtomicWord for a machine-sized pointer. It will use the Atomic32 or +// Atomic64 routines below, depending on your architecture. +typedef intptr_t AtomicWord; + +// Atomically execute: +// result = *ptr; +// if (*ptr == old_value) +// *ptr = new_value; +// return result; +// +// I.e., replace "*ptr" with "new_value" if "*ptr" used to be "old_value". +// Always return the old value of "*ptr" +// +// This routine implies no memory barriers. +Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value); + +// Atomically store new_value into *ptr, returning the previous value held in +// *ptr. This routine implies no memory barriers. +Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, Atomic32 new_value); + +// Atomically increment *ptr by "increment". Returns the new value of +// *ptr with the increment applied. This routine implies no memory barriers. +Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, Atomic32 increment); + +Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment); + +// These following lower-level operations are typically useful only to people +// implementing higher-level synchronization operations like spinlocks, +// mutexes, and condition-variables. They combine CompareAndSwap(), a load, or +// a store with appropriate memory-ordering instructions. "Acquire" operations +// ensure that no later memory access can be reordered ahead of the operation. +// "Release" operations ensure that no previous memory access can be reordered +// after the operation. "Barrier" operations have both "Acquire" and "Release" +// semantics. A MemoryBarrier() has "Barrier" semantics, but does no memory +// access. +Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value); +Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value); + +void MemoryBarrier(); +void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value); +void Acquire_Store(volatile Atomic32* ptr, Atomic32 value); +void Release_Store(volatile Atomic32* ptr, Atomic32 value); + +Atomic32 NoBarrier_Load(volatile const Atomic32* ptr); +Atomic32 Acquire_Load(volatile const Atomic32* ptr); +Atomic32 Release_Load(volatile const Atomic32* ptr); + +// 64-bit atomic operations (only available on 64-bit processors). +#ifdef ARCH_CPU_64_BITS +Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value); +Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, Atomic64 new_value); +Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment); +Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment); + +Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value); +Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value); +void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value); +void Acquire_Store(volatile Atomic64* ptr, Atomic64 value); +void Release_Store(volatile Atomic64* ptr, Atomic64 value); +Atomic64 NoBarrier_Load(volatile const Atomic64* ptr); +Atomic64 Acquire_Load(volatile const Atomic64* ptr); +Atomic64 Release_Load(volatile const Atomic64* ptr); +#endif // ARCH_CPU_64_BITS + +} // namespace subtle +} // namespace base + +#if defined(OS_WIN) +// TODO(jfb): Try to use base/atomicops_internals_portable.h everywhere. +// https://crbug.com/559247. +# include "base/atomicops_internals_x86_msvc.h" +#else +# include "base/atomicops_internals_portable.h" +#endif + +// On some platforms we need additional declarations to make +// AtomicWord compatible with our other Atomic* types. +#if defined(OS_MACOSX) || defined(OS_OPENBSD) +#include "base/atomicops_internals_atomicword_compat.h" +#endif + +#endif // BASE_ATOMICOPS_H_ diff --git a/native/zucchini/vendor/libchrome/base/atomicops_internals_portable.h b/native/zucchini/vendor/libchrome/base/atomicops_internals_portable.h new file mode 100644 index 0000000..ee034de --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/atomicops_internals_portable.h @@ -0,0 +1,229 @@ +// Copyright (c) 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file is an internal atomic implementation, use atomicops.h instead. +// +// This implementation uses C++11 atomics' member functions. The code base is +// currently written assuming atomicity revolves around accesses instead of +// C++11's memory locations. The burden is on the programmer to ensure that all +// memory locations accessed atomically are never accessed non-atomically (tsan +// should help with this). +// +// TODO(jfb) Modify the atomicops.h API and user code to declare atomic +// locations as truly atomic. See the static_assert below. +// +// Of note in this implementation: +// * All NoBarrier variants are implemented as relaxed. +// * All Barrier variants are implemented as sequentially-consistent. +// * Compare exchange's failure ordering is always the same as the success one +// (except for release, which fails as relaxed): using a weaker ordering is +// only valid under certain uses of compare exchange. +// * Acquire store doesn't exist in the C11 memory model, it is instead +// implemented as a relaxed store followed by a sequentially consistent +// fence. +// * Release load doesn't exist in the C11 memory model, it is instead +// implemented as sequentially consistent fence followed by a relaxed load. +// * Atomic increment is expected to return the post-incremented value, whereas +// C11 fetch add returns the previous value. The implementation therefore +// needs to increment twice (which the compiler should be able to detect and +// optimize). + +#ifndef BASE_ATOMICOPS_INTERNALS_PORTABLE_H_ +#define BASE_ATOMICOPS_INTERNALS_PORTABLE_H_ + +#include + +#include "build/build_config.h" + +namespace base { +namespace subtle { + +// This implementation is transitional and maintains the original API for +// atomicops.h. This requires casting memory locations to the atomic types, and +// assumes that the API and the C++11 implementation are layout-compatible, +// which isn't true for all implementations or hardware platforms. The static +// assertion should detect this issue, were it to fire then this header +// shouldn't be used. +// +// TODO(jfb) If this header manages to stay committed then the API should be +// modified, and all call sites updated. +typedef volatile std::atomic* AtomicLocation32; +static_assert(sizeof(*(AtomicLocation32) nullptr) == sizeof(Atomic32), + "incompatible 32-bit atomic layout"); + +inline void MemoryBarrier() { +#if defined(__GLIBCXX__) + // Work around libstdc++ bug 51038 where atomic_thread_fence was declared but + // not defined, leading to the linker complaining about undefined references. + __atomic_thread_fence(std::memory_order_seq_cst); +#else + std::atomic_thread_fence(std::memory_order_seq_cst); +#endif +} + +inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + ((AtomicLocation32)ptr) + ->compare_exchange_strong(old_value, + new_value, + std::memory_order_relaxed, + std::memory_order_relaxed); + return old_value; +} + +inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, + Atomic32 new_value) { + return ((AtomicLocation32)ptr) + ->exchange(new_value, std::memory_order_relaxed); +} + +inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { + return increment + + ((AtomicLocation32)ptr) + ->fetch_add(increment, std::memory_order_relaxed); +} + +inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { + return increment + ((AtomicLocation32)ptr)->fetch_add(increment); +} + +inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + ((AtomicLocation32)ptr) + ->compare_exchange_strong(old_value, + new_value, + std::memory_order_acquire, + std::memory_order_acquire); + return old_value; +} + +inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + ((AtomicLocation32)ptr) + ->compare_exchange_strong(old_value, + new_value, + std::memory_order_release, + std::memory_order_relaxed); + return old_value; +} + +inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { + ((AtomicLocation32)ptr)->store(value, std::memory_order_relaxed); +} + +inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { + ((AtomicLocation32)ptr)->store(value, std::memory_order_relaxed); + MemoryBarrier(); +} + +inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) { + ((AtomicLocation32)ptr)->store(value, std::memory_order_release); +} + +inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { + return ((AtomicLocation32)ptr)->load(std::memory_order_relaxed); +} + +inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) { + return ((AtomicLocation32)ptr)->load(std::memory_order_acquire); +} + +inline Atomic32 Release_Load(volatile const Atomic32* ptr) { + MemoryBarrier(); + return ((AtomicLocation32)ptr)->load(std::memory_order_relaxed); +} + +#if defined(ARCH_CPU_64_BITS) + +typedef volatile std::atomic* AtomicLocation64; +static_assert(sizeof(*(AtomicLocation64) nullptr) == sizeof(Atomic64), + "incompatible 64-bit atomic layout"); + +inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + ((AtomicLocation64)ptr) + ->compare_exchange_strong(old_value, + new_value, + std::memory_order_relaxed, + std::memory_order_relaxed); + return old_value; +} + +inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, + Atomic64 new_value) { + return ((AtomicLocation64)ptr) + ->exchange(new_value, std::memory_order_relaxed); +} + +inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, + Atomic64 increment) { + return increment + + ((AtomicLocation64)ptr) + ->fetch_add(increment, std::memory_order_relaxed); +} + +inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, + Atomic64 increment) { + return increment + ((AtomicLocation64)ptr)->fetch_add(increment); +} + +inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + ((AtomicLocation64)ptr) + ->compare_exchange_strong(old_value, + new_value, + std::memory_order_acquire, + std::memory_order_acquire); + return old_value; +} + +inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + ((AtomicLocation64)ptr) + ->compare_exchange_strong(old_value, + new_value, + std::memory_order_release, + std::memory_order_relaxed); + return old_value; +} + +inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) { + ((AtomicLocation64)ptr)->store(value, std::memory_order_relaxed); +} + +inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) { + ((AtomicLocation64)ptr)->store(value, std::memory_order_relaxed); + MemoryBarrier(); +} + +inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) { + ((AtomicLocation64)ptr)->store(value, std::memory_order_release); +} + +inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { + return ((AtomicLocation64)ptr)->load(std::memory_order_relaxed); +} + +inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) { + return ((AtomicLocation64)ptr)->load(std::memory_order_acquire); +} + +inline Atomic64 Release_Load(volatile const Atomic64* ptr) { + MemoryBarrier(); + return ((AtomicLocation64)ptr)->load(std::memory_order_relaxed); +} + +#endif // defined(ARCH_CPU_64_BITS) +} // namespace subtle +} // namespace base + +#endif // BASE_ATOMICOPS_INTERNALS_PORTABLE_H_ diff --git a/native/zucchini/vendor/libchrome/base/base_export.h b/native/zucchini/vendor/libchrome/base/base_export.h new file mode 100644 index 0000000..cf7ebd7 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/base_export.h @@ -0,0 +1,29 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_BASE_EXPORT_H_ +#define BASE_BASE_EXPORT_H_ + +#if defined(COMPONENT_BUILD) +#if defined(WIN32) + +#if defined(BASE_IMPLEMENTATION) +#define BASE_EXPORT __declspec(dllexport) +#else +#define BASE_EXPORT __declspec(dllimport) +#endif // defined(BASE_IMPLEMENTATION) + +#else // defined(WIN32) +#if defined(BASE_IMPLEMENTATION) +#define BASE_EXPORT __attribute__((visibility("default"))) +#else +#define BASE_EXPORT +#endif // defined(BASE_IMPLEMENTATION) +#endif + +#else // defined(COMPONENT_BUILD) +#define BASE_EXPORT +#endif + +#endif // BASE_BASE_EXPORT_H_ diff --git a/native/zucchini/vendor/libchrome/base/base_switches.h b/native/zucchini/vendor/libchrome/base/base_switches.h new file mode 100644 index 0000000..4ef070d --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/base_switches.h @@ -0,0 +1,51 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Defines all the "base" command-line switches. + +#ifndef BASE_BASE_SWITCHES_H_ +#define BASE_BASE_SWITCHES_H_ + +#include "build/build_config.h" + +namespace switches { + +extern const char kDisableBackgroundTasks[]; +extern const char kDisableBreakpad[]; +extern const char kDisableFeatures[]; +extern const char kDisableLowEndDeviceMode[]; +extern const char kEnableCrashReporter[]; +extern const char kEnableFeatures[]; +extern const char kEnableLowEndDeviceMode[]; +extern const char kForceFieldTrials[]; +extern const char kFullMemoryCrashReport[]; +extern const char kNoErrorDialogs[]; +extern const char kProfilingFile[]; +extern const char kTestChildProcess[]; +extern const char kTestDoNotInitializeIcu[]; +extern const char kTraceToFile[]; +extern const char kTraceToFileName[]; +extern const char kV[]; +extern const char kVModule[]; +extern const char kWaitForDebugger[]; + +#if defined(OS_WIN) +extern const char kDisableUsbKeyboardDetect[]; +#endif + +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) +extern const char kDisableDevShmUsage[]; +#endif + +#if defined(OS_POSIX) +extern const char kEnableCrashReporterForTesting[]; +#endif + +#if defined(OS_ANDROID) +extern const char kOrderfileMemoryOptimization[]; +#endif + +} // namespace switches + +#endif // BASE_BASE_SWITCHES_H_ diff --git a/native/zucchini/vendor/libchrome/base/bind.h b/native/zucchini/vendor/libchrome/base/bind.h new file mode 100644 index 0000000..66d5d82 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/bind.h @@ -0,0 +1,482 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_BIND_H_ +#define BASE_BIND_H_ + +#include + +#include "base/bind_internal.h" +#include "base/compiler_specific.h" +#include "build/build_config.h" + +#if defined(OS_MACOSX) && !HAS_FEATURE(objc_arc) +#include "base/mac/scoped_block.h" +#endif + +// ----------------------------------------------------------------------------- +// Usage documentation +// ----------------------------------------------------------------------------- +// +// Overview: +// base::BindOnce() and base::BindRepeating() are helpers for creating +// base::OnceCallback and base::RepeatingCallback objects respectively. +// +// For a runnable object of n-arity, the base::Bind*() family allows partial +// application of the first m arguments. The remaining n - m arguments must be +// passed when invoking the callback with Run(). +// +// // The first argument is bound at callback creation; the remaining +// // two must be passed when calling Run() on the callback object. +// base::OnceCallback cb = base::BindOnce( +// [](short x, int y, long z) { return x * y * z; }, 42); +// +// When binding to a method, the receiver object must also be specified at +// callback creation time. When Run() is invoked, the method will be invoked on +// the specified receiver object. +// +// class C : public base::RefCounted { void F(); }; +// auto instance = base::MakeRefCounted(); +// auto cb = base::BindOnce(&C::F, instance); +// cb.Run(); // Identical to instance->F() +// +// base::Bind is currently a type alias for base::BindRepeating(). In the +// future, we expect to flip this to default to base::BindOnce(). +// +// See //docs/callback.md for the full documentation. +// +// ----------------------------------------------------------------------------- +// Implementation notes +// ----------------------------------------------------------------------------- +// +// If you're reading the implementation, before proceeding further, you should +// read the top comment of base/bind_internal.h for a definition of common +// terms and concepts. + +namespace base { + +namespace internal { + +// IsOnceCallback is a std::true_type if |T| is a OnceCallback. +template +struct IsOnceCallback : std::false_type {}; + +template +struct IsOnceCallback> : std::true_type {}; + +// Helper to assert that parameter |i| of type |Arg| can be bound, which means: +// - |Arg| can be retained internally as |Storage|. +// - |Arg| can be forwarded as |Unwrapped| to |Param|. +template +struct AssertConstructible { + private: + static constexpr bool param_is_forwardable = + std::is_constructible::value; + // Unlike the check for binding into storage below, the check for + // forwardability drops the const qualifier for repeating callbacks. This is + // to try to catch instances where std::move()--which forwards as a const + // reference with repeating callbacks--is used instead of base::Passed(). + static_assert( + param_is_forwardable || + !std::is_constructible&&>::value, + "Bound argument |i| is move-only but will be forwarded by copy. " + "Ensure |Arg| is bound using base::Passed(), not std::move()."); + static_assert( + param_is_forwardable, + "Bound argument |i| of type |Arg| cannot be forwarded as " + "|Unwrapped| to the bound functor, which declares it as |Param|."); + + static constexpr bool arg_is_storable = + std::is_constructible::value; + static_assert(arg_is_storable || + !std::is_constructible&&>::value, + "Bound argument |i| is move-only but will be bound by copy. " + "Ensure |Arg| is mutable and bound using std::move()."); + static_assert(arg_is_storable, + "Bound argument |i| of type |Arg| cannot be converted and " + "bound as |Storage|."); +}; + +// Takes three same-length TypeLists, and applies AssertConstructible for each +// triples. +template +struct AssertBindArgsValidity; + +template +struct AssertBindArgsValidity, + TypeList, + TypeList, + TypeList> + : AssertConstructible, Unwrapped, Params>... { + static constexpr bool ok = true; +}; + +// The implementation of TransformToUnwrappedType below. +template +struct TransformToUnwrappedTypeImpl; + +template +struct TransformToUnwrappedTypeImpl { + using StoredType = std::decay_t; + using ForwardType = StoredType&&; + using Unwrapped = decltype(Unwrap(std::declval())); +}; + +template +struct TransformToUnwrappedTypeImpl { + using StoredType = std::decay_t; + using ForwardType = const StoredType&; + using Unwrapped = decltype(Unwrap(std::declval())); +}; + +// Transform |T| into `Unwrapped` type, which is passed to the target function. +// Example: +// In is_once == true case, +// `int&&` -> `int&&`, +// `const int&` -> `int&&`, +// `OwnedWrapper&` -> `int*&&`. +// In is_once == false case, +// `int&&` -> `const int&`, +// `const int&` -> `const int&`, +// `OwnedWrapper&` -> `int* const &`. +template +using TransformToUnwrappedType = + typename TransformToUnwrappedTypeImpl::Unwrapped; + +// Transforms |Args| into `Unwrapped` types, and packs them into a TypeList. +// If |is_method| is true, tries to dereference the first argument to support +// smart pointers. +template +struct MakeUnwrappedTypeListImpl { + using Type = TypeList...>; +}; + +// Performs special handling for this pointers. +// Example: +// int* -> int*, +// std::unique_ptr -> int*. +template +struct MakeUnwrappedTypeListImpl { + using UnwrappedReceiver = TransformToUnwrappedType; + using Type = TypeList()), + TransformToUnwrappedType...>; +}; + +template +using MakeUnwrappedTypeList = + typename MakeUnwrappedTypeListImpl::Type; + +} // namespace internal + +// Bind as OnceCallback. +template +inline OnceCallback> +BindOnce(Functor&& functor, Args&&... args) { + static_assert(!internal::IsOnceCallback>() || + (std::is_rvalue_reference() && + !std::is_const>()), + "BindOnce requires non-const rvalue for OnceCallback binding." + " I.e.: base::BindOnce(std::move(callback))."); + + // This block checks if each |args| matches to the corresponding params of the + // target function. This check does not affect the behavior of Bind, but its + // error message should be more readable. + using Helper = internal::BindTypeHelper; + using FunctorTraits = typename Helper::FunctorTraits; + using BoundArgsList = typename Helper::BoundArgsList; + using UnwrappedArgsList = + internal::MakeUnwrappedTypeList; + using BoundParamsList = typename Helper::BoundParamsList; + static_assert(internal::AssertBindArgsValidity< + std::make_index_sequence, BoundArgsList, + UnwrappedArgsList, BoundParamsList>::ok, + "The bound args need to be convertible to the target params."); + + using BindState = internal::MakeBindStateType; + using UnboundRunType = MakeUnboundRunType; + using Invoker = internal::Invoker; + using CallbackType = OnceCallback; + + // Store the invoke func into PolymorphicInvoke before casting it to + // InvokeFuncStorage, so that we can ensure its type matches to + // PolymorphicInvoke, to which CallbackType will cast back. + using PolymorphicInvoke = typename CallbackType::PolymorphicInvoke; + PolymorphicInvoke invoke_func = &Invoker::RunOnce; + + using InvokeFuncStorage = internal::BindStateBase::InvokeFuncStorage; + return CallbackType(new BindState( + reinterpret_cast(invoke_func), + std::forward(functor), + std::forward(args)...)); +} + +// Bind as RepeatingCallback. +template +inline RepeatingCallback> +BindRepeating(Functor&& functor, Args&&... args) { + static_assert( + !internal::IsOnceCallback>(), + "BindRepeating cannot bind OnceCallback. Use BindOnce with std::move()."); + + // This block checks if each |args| matches to the corresponding params of the + // target function. This check does not affect the behavior of Bind, but its + // error message should be more readable. + using Helper = internal::BindTypeHelper; + using FunctorTraits = typename Helper::FunctorTraits; + using BoundArgsList = typename Helper::BoundArgsList; + using UnwrappedArgsList = + internal::MakeUnwrappedTypeList; + using BoundParamsList = typename Helper::BoundParamsList; + static_assert(internal::AssertBindArgsValidity< + std::make_index_sequence, BoundArgsList, + UnwrappedArgsList, BoundParamsList>::ok, + "The bound args need to be convertible to the target params."); + + using BindState = internal::MakeBindStateType; + using UnboundRunType = MakeUnboundRunType; + using Invoker = internal::Invoker; + using CallbackType = RepeatingCallback; + + // Store the invoke func into PolymorphicInvoke before casting it to + // InvokeFuncStorage, so that we can ensure its type matches to + // PolymorphicInvoke, to which CallbackType will cast back. + using PolymorphicInvoke = typename CallbackType::PolymorphicInvoke; + PolymorphicInvoke invoke_func = &Invoker::Run; + + using InvokeFuncStorage = internal::BindStateBase::InvokeFuncStorage; + return CallbackType(new BindState( + reinterpret_cast(invoke_func), + std::forward(functor), + std::forward(args)...)); +} + +// Unannotated Bind. +// TODO(tzik): Deprecate this and migrate to OnceCallback and +// RepeatingCallback, once they get ready. +template +inline Callback> +Bind(Functor&& functor, Args&&... args) { + return base::BindRepeating(std::forward(functor), + std::forward(args)...); +} + +// Special cases for binding to a base::Callback without extra bound arguments. +template +OnceCallback BindOnce(OnceCallback closure) { + return closure; +} + +template +RepeatingCallback BindRepeating( + RepeatingCallback closure) { + return closure; +} + +template +Callback Bind(Callback closure) { + return closure; +} + +// Unretained() allows Bind() to bind a non-refcounted class, and to disable +// refcounting on arguments that are refcounted objects. +// +// EXAMPLE OF Unretained(): +// +// class Foo { +// public: +// void func() { cout << "Foo:f" << endl; } +// }; +// +// // In some function somewhere. +// Foo foo; +// Closure foo_callback = +// Bind(&Foo::func, Unretained(&foo)); +// foo_callback.Run(); // Prints "Foo:f". +// +// Without the Unretained() wrapper on |&foo|, the above call would fail +// to compile because Foo does not support the AddRef() and Release() methods. +template +static inline internal::UnretainedWrapper Unretained(T* o) { + return internal::UnretainedWrapper(o); +} + +// RetainedRef() accepts a ref counted object and retains a reference to it. +// When the callback is called, the object is passed as a raw pointer. +// +// EXAMPLE OF RetainedRef(): +// +// void foo(RefCountedBytes* bytes) {} +// +// scoped_refptr bytes = ...; +// Closure callback = Bind(&foo, base::RetainedRef(bytes)); +// callback.Run(); +// +// Without RetainedRef, the scoped_refptr would try to implicitly convert to +// a raw pointer and fail compilation: +// +// Closure callback = Bind(&foo, bytes); // ERROR! +template +static inline internal::RetainedRefWrapper RetainedRef(T* o) { + return internal::RetainedRefWrapper(o); +} +template +static inline internal::RetainedRefWrapper RetainedRef(scoped_refptr o) { + return internal::RetainedRefWrapper(std::move(o)); +} + +// ConstRef() allows binding a constant reference to an argument rather +// than a copy. +// +// EXAMPLE OF ConstRef(): +// +// void foo(int arg) { cout << arg << endl } +// +// int n = 1; +// Closure no_ref = Bind(&foo, n); +// Closure has_ref = Bind(&foo, ConstRef(n)); +// +// no_ref.Run(); // Prints "1" +// has_ref.Run(); // Prints "1" +// +// n = 2; +// no_ref.Run(); // Prints "1" +// has_ref.Run(); // Prints "2" +// +// Note that because ConstRef() takes a reference on |n|, |n| must outlive all +// its bound callbacks. +template +static inline internal::ConstRefWrapper ConstRef(const T& o) { + return internal::ConstRefWrapper(o); +} + +// Owned() transfers ownership of an object to the Callback resulting from +// bind; the object will be deleted when the Callback is deleted. +// +// EXAMPLE OF Owned(): +// +// void foo(int* arg) { cout << *arg << endl } +// +// int* pn = new int(1); +// Closure foo_callback = Bind(&foo, Owned(pn)); +// +// foo_callback.Run(); // Prints "1" +// foo_callback.Run(); // Prints "1" +// *n = 2; +// foo_callback.Run(); // Prints "2" +// +// foo_callback.Reset(); // |pn| is deleted. Also will happen when +// // |foo_callback| goes out of scope. +// +// Without Owned(), someone would have to know to delete |pn| when the last +// reference to the Callback is deleted. +template +static inline internal::OwnedWrapper Owned(T* o) { + return internal::OwnedWrapper(o); +} + +// Passed() is for transferring movable-but-not-copyable types (eg. unique_ptr) +// through a Callback. Logically, this signifies a destructive transfer of +// the state of the argument into the target function. Invoking +// Callback::Run() twice on a Callback that was created with a Passed() +// argument will CHECK() because the first invocation would have already +// transferred ownership to the target function. +// +// Note that Passed() is not necessary with BindOnce(), as std::move() does the +// same thing. Avoid Passed() in favor of std::move() with BindOnce(). +// +// EXAMPLE OF Passed(): +// +// void TakesOwnership(std::unique_ptr arg) { } +// std::unique_ptr CreateFoo() { return std::make_unique(); +// } +// +// auto f = std::make_unique(); +// +// // |cb| is given ownership of Foo(). |f| is now NULL. +// // You can use std::move(f) in place of &f, but it's more verbose. +// Closure cb = Bind(&TakesOwnership, Passed(&f)); +// +// // Run was never called so |cb| still owns Foo() and deletes +// // it on Reset(). +// cb.Reset(); +// +// // |cb| is given a new Foo created by CreateFoo(). +// cb = Bind(&TakesOwnership, Passed(CreateFoo())); +// +// // |arg| in TakesOwnership() is given ownership of Foo(). |cb| +// // no longer owns Foo() and, if reset, would not delete Foo(). +// cb.Run(); // Foo() is now transferred to |arg| and deleted. +// cb.Run(); // This CHECK()s since Foo() already been used once. +// +// We offer 2 syntaxes for calling Passed(). The first takes an rvalue and +// is best suited for use with the return value of a function or other temporary +// rvalues. The second takes a pointer to the scoper and is just syntactic sugar +// to avoid having to write Passed(std::move(scoper)). +// +// Both versions of Passed() prevent T from being an lvalue reference. The first +// via use of enable_if, and the second takes a T* which will not bind to T&. +template ::value>* = nullptr> +static inline internal::PassedWrapper Passed(T&& scoper) { + return internal::PassedWrapper(std::move(scoper)); +} +template +static inline internal::PassedWrapper Passed(T* scoper) { + return internal::PassedWrapper(std::move(*scoper)); +} + +// IgnoreResult() is used to adapt a function or Callback with a return type to +// one with a void return. This is most useful if you have a function with, +// say, a pesky ignorable bool return that you want to use with PostTask or +// something else that expect a Callback with a void return. +// +// EXAMPLE OF IgnoreResult(): +// +// int DoSomething(int arg) { cout << arg << endl; } +// +// // Assign to a Callback with a void return type. +// Callback cb = Bind(IgnoreResult(&DoSomething)); +// cb->Run(1); // Prints "1". +// +// // Prints "1" on |ml|. +// ml->PostTask(FROM_HERE, Bind(IgnoreResult(&DoSomething), 1); +template +static inline internal::IgnoreResultHelper IgnoreResult(T data) { + return internal::IgnoreResultHelper(std::move(data)); +} + +#if defined(OS_MACOSX) && !HAS_FEATURE(objc_arc) + +// RetainBlock() is used to adapt an Objective-C block when Automated Reference +// Counting (ARC) is disabled. This is unnecessary when ARC is enabled, as the +// BindOnce and BindRepeating already support blocks then. +// +// EXAMPLE OF RetainBlock(): +// +// // Wrap the block and bind it to a callback. +// Callback cb = Bind(RetainBlock(^(int n) { NSLog(@"%d", n); })); +// cb.Run(1); // Logs "1". +template +base::mac::ScopedBlock RetainBlock(R (^block)(Args...)) { + return base::mac::ScopedBlock(block, + base::scoped_policy::RETAIN); +} + +#endif // defined(OS_MACOSX) && !HAS_FEATURE(objc_arc) + +} // namespace base + +#endif // BASE_BIND_H_ diff --git a/native/zucchini/vendor/libchrome/base/bind_internal.h b/native/zucchini/vendor/libchrome/base/bind_internal.h new file mode 100644 index 0000000..4ebecfa --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/bind_internal.h @@ -0,0 +1,971 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_BIND_INTERNAL_H_ +#define BASE_BIND_INTERNAL_H_ + +#include + +#include +#include + +#include "base/callback_internal.h" +#include "base/compiler_specific.h" +#include "base/memory/raw_scoped_refptr_mismatch_checker.h" +#include "base/memory/weak_ptr.h" +#include "base/template_util.h" +#include "build/build_config.h" + +#if defined(OS_MACOSX) && !HAS_FEATURE(objc_arc) +#include "base/mac/scoped_block.h" +#endif + +// See base/callback.h for user documentation. +// +// +// CONCEPTS: +// Functor -- A movable type representing something that should be called. +// All function pointers and Callback<> are functors even if the +// invocation syntax differs. +// RunType -- A function type (as opposed to function _pointer_ type) for +// a Callback<>::Run(). Usually just a convenience typedef. +// (Bound)Args -- A set of types that stores the arguments. +// +// Types: +// ForceVoidReturn<> -- Helper class for translating function signatures to +// equivalent forms with a "void" return type. +// FunctorTraits<> -- Type traits used to determine the correct RunType and +// invocation manner for a Functor. This is where function +// signature adapters are applied. +// InvokeHelper<> -- Take a Functor + arguments and actully invokes it. +// Handle the differing syntaxes needed for WeakPtr<> +// support. This is separate from Invoker to avoid creating +// multiple version of Invoker<>. +// Invoker<> -- Unwraps the curried parameters and executes the Functor. +// BindState<> -- Stores the curried parameters, and is the main entry point +// into the Bind() system. + +namespace base { + +template +struct IsWeakReceiver; + +template +struct BindUnwrapTraits; + +template +struct CallbackCancellationTraits; + +namespace internal { + +template +struct FunctorTraits; + +template +class UnretainedWrapper { + public: + explicit UnretainedWrapper(T* o) : ptr_(o) {} + T* get() const { return ptr_; } + + private: + T* ptr_; +}; + +template +class ConstRefWrapper { + public: + explicit ConstRefWrapper(const T& o) : ptr_(&o) {} + const T& get() const { return *ptr_; } + + private: + const T* ptr_; +}; + +template +class RetainedRefWrapper { + public: + explicit RetainedRefWrapper(T* o) : ptr_(o) {} + explicit RetainedRefWrapper(scoped_refptr o) : ptr_(std::move(o)) {} + T* get() const { return ptr_.get(); } + + private: + scoped_refptr ptr_; +}; + +template +struct IgnoreResultHelper { + explicit IgnoreResultHelper(T functor) : functor_(std::move(functor)) {} + explicit operator bool() const { return !!functor_; } + + T functor_; +}; + +// An alternate implementation is to avoid the destructive copy, and instead +// specialize ParamTraits<> for OwnedWrapper<> to change the StorageType to +// a class that is essentially a std::unique_ptr<>. +// +// The current implementation has the benefit though of leaving ParamTraits<> +// fully in callback_internal.h as well as avoiding type conversions during +// storage. +template +class OwnedWrapper { + public: + explicit OwnedWrapper(T* o) : ptr_(o) {} + ~OwnedWrapper() { delete ptr_; } + T* get() const { return ptr_; } + OwnedWrapper(OwnedWrapper&& other) { + ptr_ = other.ptr_; + other.ptr_ = NULL; + } + + private: + mutable T* ptr_; +}; + +// PassedWrapper is a copyable adapter for a scoper that ignores const. +// +// It is needed to get around the fact that Bind() takes a const reference to +// all its arguments. Because Bind() takes a const reference to avoid +// unnecessary copies, it is incompatible with movable-but-not-copyable +// types; doing a destructive "move" of the type into Bind() would violate +// the const correctness. +// +// This conundrum cannot be solved without either C++11 rvalue references or +// a O(2^n) blowup of Bind() templates to handle each combination of regular +// types and movable-but-not-copyable types. Thus we introduce a wrapper type +// that is copyable to transmit the correct type information down into +// BindState<>. Ignoring const in this type makes sense because it is only +// created when we are explicitly trying to do a destructive move. +// +// Two notes: +// 1) PassedWrapper supports any type that has a move constructor, however +// the type will need to be specifically whitelisted in order for it to be +// bound to a Callback. We guard this explicitly at the call of Passed() +// to make for clear errors. Things not given to Passed() will be forwarded +// and stored by value which will not work for general move-only types. +// 2) is_valid_ is distinct from NULL because it is valid to bind a "NULL" +// scoper to a Callback and allow the Callback to execute once. +template +class PassedWrapper { + public: + explicit PassedWrapper(T&& scoper) + : is_valid_(true), scoper_(std::move(scoper)) {} + PassedWrapper(PassedWrapper&& other) + : is_valid_(other.is_valid_), scoper_(std::move(other.scoper_)) {} + T Take() const { + CHECK(is_valid_); + is_valid_ = false; + return std::move(scoper_); + } + + private: + mutable bool is_valid_; + mutable T scoper_; +}; + +template +using Unwrapper = BindUnwrapTraits>; + +template +decltype(auto) Unwrap(T&& o) { + return Unwrapper::Unwrap(std::forward(o)); +} + +// IsWeakMethod is a helper that determine if we are binding a WeakPtr<> to a +// method. It is used internally by Bind() to select the correct +// InvokeHelper that will no-op itself in the event the WeakPtr<> for +// the target object is invalidated. +// +// The first argument should be the type of the object that will be received by +// the method. +template +struct IsWeakMethod : std::false_type {}; + +template +struct IsWeakMethod : IsWeakReceiver {}; + +// Packs a list of types to hold them in a single type. +template +struct TypeList {}; + +// Used for DropTypeListItem implementation. +template +struct DropTypeListItemImpl; + +// Do not use enable_if and SFINAE here to avoid MSVC2013 compile failure. +template +struct DropTypeListItemImpl> + : DropTypeListItemImpl> {}; + +template +struct DropTypeListItemImpl<0, TypeList> { + using Type = TypeList; +}; + +template <> +struct DropTypeListItemImpl<0, TypeList<>> { + using Type = TypeList<>; +}; + +// A type-level function that drops |n| list item from given TypeList. +template +using DropTypeListItem = typename DropTypeListItemImpl::Type; + +// Used for TakeTypeListItem implementation. +template +struct TakeTypeListItemImpl; + +// Do not use enable_if and SFINAE here to avoid MSVC2013 compile failure. +template +struct TakeTypeListItemImpl, Accum...> + : TakeTypeListItemImpl, Accum..., T> {}; + +template +struct TakeTypeListItemImpl<0, TypeList, Accum...> { + using Type = TypeList; +}; + +template +struct TakeTypeListItemImpl<0, TypeList<>, Accum...> { + using Type = TypeList; +}; + +// A type-level function that takes first |n| list item from given TypeList. +// E.g. TakeTypeListItem<3, TypeList> is evaluated to +// TypeList. +template +using TakeTypeListItem = typename TakeTypeListItemImpl::Type; + +// Used for ConcatTypeLists implementation. +template +struct ConcatTypeListsImpl; + +template +struct ConcatTypeListsImpl, TypeList> { + using Type = TypeList; +}; + +// A type-level function that concats two TypeLists. +template +using ConcatTypeLists = typename ConcatTypeListsImpl::Type; + +// Used for MakeFunctionType implementation. +template +struct MakeFunctionTypeImpl; + +template +struct MakeFunctionTypeImpl> { + // MSVC 2013 doesn't support Type Alias of function types. + // Revisit this after we update it to newer version. + typedef R Type(Args...); +}; + +// A type-level function that constructs a function type that has |R| as its +// return type and has TypeLists items as its arguments. +template +using MakeFunctionType = typename MakeFunctionTypeImpl::Type; + +// Used for ExtractArgs and ExtractReturnType. +template +struct ExtractArgsImpl; + +template +struct ExtractArgsImpl { + using ReturnType = R; + using ArgsList = TypeList; +}; + +// A type-level function that extracts function arguments into a TypeList. +// E.g. ExtractArgs is evaluated to TypeList. +template +using ExtractArgs = typename ExtractArgsImpl::ArgsList; + +// A type-level function that extracts the return type of a function. +// E.g. ExtractReturnType is evaluated to R. +template +using ExtractReturnType = typename ExtractArgsImpl::ReturnType; + +template +struct ExtractCallableRunTypeImpl; + +template +struct ExtractCallableRunTypeImpl { + using Type = R(Args...); +}; + +template +struct ExtractCallableRunTypeImpl { + using Type = R(Args...); +}; + +// Evaluated to RunType of the given callable type. +// Example: +// auto f = [](int, char*) { return 0.1; }; +// ExtractCallableRunType +// is evaluated to +// double(int, char*); +template +using ExtractCallableRunType = + typename ExtractCallableRunTypeImpl::Type; + +// IsCallableObject is std::true_type if |Functor| has operator(). +// Otherwise, it's std::false_type. +// Example: +// IsCallableObject::value is false. +// +// struct Foo {}; +// IsCallableObject::value is false. +// +// int i = 0; +// auto f = [i]() {}; +// IsCallableObject::value is false. +template +struct IsCallableObject : std::false_type {}; + +template +struct IsCallableObject> + : std::true_type {}; + +// HasRefCountedTypeAsRawPtr selects true_type when any of the |Args| is a raw +// pointer to a RefCounted type. +// Implementation note: This non-specialized case handles zero-arity case only. +// Non-zero-arity cases should be handled by the specialization below. +template +struct HasRefCountedTypeAsRawPtr : std::false_type {}; + +// Implementation note: Select true_type if the first parameter is a raw pointer +// to a RefCounted type. Otherwise, skip the first parameter and check rest of +// parameters recursively. +template +struct HasRefCountedTypeAsRawPtr + : std::conditional_t::value, + std::true_type, + HasRefCountedTypeAsRawPtr> {}; + +// ForceVoidReturn<> +// +// Set of templates that support forcing the function return type to void. +template +struct ForceVoidReturn; + +template +struct ForceVoidReturn { + using RunType = void(Args...); +}; + +// FunctorTraits<> +// +// See description at top of file. +template +struct FunctorTraits; + +// For empty callable types. +// This specialization is intended to allow binding captureless lambdas by +// base::Bind(), based on the fact that captureless lambdas are empty while +// capturing lambdas are not. This also allows any functors as far as it's an +// empty class. +// Example: +// +// // Captureless lambdas are allowed. +// []() {return 42;}; +// +// // Capturing lambdas are *not* allowed. +// int x; +// [x]() {return x;}; +// +// // Any empty class with operator() is allowed. +// struct Foo { +// void operator()() const {} +// // No non-static member variable and no virtual functions. +// }; +template +struct FunctorTraits::value && + std::is_empty::value>> { + using RunType = ExtractCallableRunType; + static constexpr bool is_method = false; + static constexpr bool is_nullable = false; + + template + static ExtractReturnType Invoke(RunFunctor&& functor, + RunArgs&&... args) { + return std::forward(functor)(std::forward(args)...); + } +}; + +// For functions. +template +struct FunctorTraits { + using RunType = R(Args...); + static constexpr bool is_method = false; + static constexpr bool is_nullable = true; + + template + static R Invoke(Function&& function, RunArgs&&... args) { + return std::forward(function)(std::forward(args)...); + } +}; + +#if defined(OS_WIN) && !defined(ARCH_CPU_X86_64) + +// For functions. +template +struct FunctorTraits { + using RunType = R(Args...); + static constexpr bool is_method = false; + static constexpr bool is_nullable = true; + + template + static R Invoke(R(__stdcall* function)(Args...), RunArgs&&... args) { + return function(std::forward(args)...); + } +}; + +// For functions. +template +struct FunctorTraits { + using RunType = R(Args...); + static constexpr bool is_method = false; + static constexpr bool is_nullable = true; + + template + static R Invoke(R(__fastcall* function)(Args...), RunArgs&&... args) { + return function(std::forward(args)...); + } +}; + +#endif // defined(OS_WIN) && !defined(ARCH_CPU_X86_64) + +#if defined(OS_MACOSX) + +// Support for Objective-C blocks. There are two implementation depending +// on whether Automated Reference Counting (ARC) is enabled. When ARC is +// enabled, then the block itself can be bound as the compiler will ensure +// its lifetime will be correctly managed. Otherwise, require the block to +// be wrapped in a base::mac::ScopedBlock (via base::RetainBlock) that will +// correctly manage the block lifetime. +// +// The two implementation ensure that the One Definition Rule (ODR) is not +// broken (it is not possible to write a template base::RetainBlock that would +// work correctly both with ARC enabled and disabled). + +#if HAS_FEATURE(objc_arc) + +template +struct FunctorTraits { + using RunType = R(Args...); + static constexpr bool is_method = false; + static constexpr bool is_nullable = true; + + template + static R Invoke(BlockType&& block, RunArgs&&... args) { + // According to LLVM documentation (§ 6.3), "local variables of automatic + // storage duration do not have precise lifetime." Use objc_precise_lifetime + // to ensure that the Objective-C block is not deallocated until it has + // finished executing even if the Callback<> is destroyed during the block + // execution. + // https://clang.llvm.org/docs/AutomaticReferenceCounting.html#precise-lifetime-semantics + __attribute__((objc_precise_lifetime)) R (^scoped_block)(Args...) = block; + return scoped_block(std::forward(args)...); + } +}; + +#else // HAS_FEATURE(objc_arc) + +template +struct FunctorTraits> { + using RunType = R(Args...); + static constexpr bool is_method = false; + static constexpr bool is_nullable = true; + + template + static R Invoke(BlockType&& block, RunArgs&&... args) { + // Copy the block to ensure that the Objective-C block is not deallocated + // until it has finished executing even if the Callback<> is destroyed + // during the block execution. + base::mac::ScopedBlock scoped_block(block); + return scoped_block.get()(std::forward(args)...); + } +}; + +#endif // HAS_FEATURE(objc_arc) +#endif // defined(OS_MACOSX) + +// For methods. +template +struct FunctorTraits { + using RunType = R(Receiver*, Args...); + static constexpr bool is_method = true; + static constexpr bool is_nullable = true; + + template + static R Invoke(Method method, + ReceiverPtr&& receiver_ptr, + RunArgs&&... args) { + return ((*receiver_ptr).*method)(std::forward(args)...); + } +}; + +// For const methods. +template +struct FunctorTraits { + using RunType = R(const Receiver*, Args...); + static constexpr bool is_method = true; + static constexpr bool is_nullable = true; + + template + static R Invoke(Method method, + ReceiverPtr&& receiver_ptr, + RunArgs&&... args) { + return ((*receiver_ptr).*method)(std::forward(args)...); + } +}; + +#ifdef __cpp_noexcept_function_type +// noexcept makes a distinct function type in C++17. +// I.e. `void(*)()` and `void(*)() noexcept` are same in pre-C++17, and +// different in C++17. +template +struct FunctorTraits : FunctorTraits { +}; + +template +struct FunctorTraits + : FunctorTraits {}; + +template +struct FunctorTraits + : FunctorTraits {}; +#endif + +// For IgnoreResults. +template +struct FunctorTraits> : FunctorTraits { + using RunType = + typename ForceVoidReturn::RunType>::RunType; + + template + static void Invoke(IgnoreResultType&& ignore_result_helper, + RunArgs&&... args) { + FunctorTraits::Invoke( + std::forward(ignore_result_helper).functor_, + std::forward(args)...); + } +}; + +// For OnceCallbacks. +template +struct FunctorTraits> { + using RunType = R(Args...); + static constexpr bool is_method = false; + static constexpr bool is_nullable = true; + + template + static R Invoke(CallbackType&& callback, RunArgs&&... args) { + DCHECK(!callback.is_null()); + return std::forward(callback).Run( + std::forward(args)...); + } +}; + +// For RepeatingCallbacks. +template +struct FunctorTraits> { + using RunType = R(Args...); + static constexpr bool is_method = false; + static constexpr bool is_nullable = true; + + template + static R Invoke(CallbackType&& callback, RunArgs&&... args) { + DCHECK(!callback.is_null()); + return std::forward(callback).Run( + std::forward(args)...); + } +}; + +template +using MakeFunctorTraits = FunctorTraits>; + +// InvokeHelper<> +// +// There are 2 logical InvokeHelper<> specializations: normal, WeakCalls. +// +// The normal type just calls the underlying runnable. +// +// WeakCalls need special syntax that is applied to the first argument to check +// if they should no-op themselves. +template +struct InvokeHelper; + +template +struct InvokeHelper { + template + static inline ReturnType MakeItSo(Functor&& functor, RunArgs&&... args) { + using Traits = MakeFunctorTraits; + return Traits::Invoke(std::forward(functor), + std::forward(args)...); + } +}; + +template +struct InvokeHelper { + // WeakCalls are only supported for functions with a void return type. + // Otherwise, the function result would be undefined if the the WeakPtr<> + // is invalidated. + static_assert(std::is_void::value, + "weak_ptrs can only bind to methods without return values"); + + template + static inline void MakeItSo(Functor&& functor, + BoundWeakPtr&& weak_ptr, + RunArgs&&... args) { + if (!weak_ptr) + return; + using Traits = MakeFunctorTraits; + Traits::Invoke(std::forward(functor), + std::forward(weak_ptr), + std::forward(args)...); + } +}; + +// Invoker<> +// +// See description at the top of the file. +template +struct Invoker; + +template +struct Invoker { + static R RunOnce(BindStateBase* base, + PassingType... unbound_args) { + // Local references to make debugger stepping easier. If in a debugger, + // you really want to warp ahead and step through the + // InvokeHelper<>::MakeItSo() call below. + StorageType* storage = static_cast(base); + static constexpr size_t num_bound_args = + std::tuple_sizebound_args_)>::value; + return RunImpl(std::move(storage->functor_), + std::move(storage->bound_args_), + std::make_index_sequence(), + std::forward(unbound_args)...); + } + + static R Run(BindStateBase* base, PassingType... unbound_args) { + // Local references to make debugger stepping easier. If in a debugger, + // you really want to warp ahead and step through the + // InvokeHelper<>::MakeItSo() call below. + const StorageType* storage = static_cast(base); + static constexpr size_t num_bound_args = + std::tuple_sizebound_args_)>::value; + return RunImpl(storage->functor_, storage->bound_args_, + std::make_index_sequence(), + std::forward(unbound_args)...); + } + + private: + template + static inline R RunImpl(Functor&& functor, + BoundArgsTuple&& bound, + std::index_sequence, + UnboundArgs&&... unbound_args) { + static constexpr bool is_method = MakeFunctorTraits::is_method; + + using DecayedArgsTuple = std::decay_t; + static constexpr bool is_weak_call = + IsWeakMethod...>(); + + return InvokeHelper::MakeItSo( + std::forward(functor), + Unwrap(std::get(std::forward(bound)))..., + std::forward(unbound_args)...); + } +}; + +// Extracts necessary type info from Functor and BoundArgs. +// Used to implement MakeUnboundRunType, BindOnce and BindRepeating. +template +struct BindTypeHelper { + static constexpr size_t num_bounds = sizeof...(BoundArgs); + using FunctorTraits = MakeFunctorTraits; + + // Example: + // When Functor is `double (Foo::*)(int, const std::string&)`, and BoundArgs + // is a template pack of `Foo*` and `int16_t`: + // - RunType is `double(Foo*, int, const std::string&)`, + // - ReturnType is `double`, + // - RunParamsList is `TypeList`, + // - BoundParamsList is `TypeList`, + // - UnboundParamsList is `TypeList`, + // - BoundArgsList is `TypeList`, + // - UnboundRunType is `double(const std::string&)`. + using RunType = typename FunctorTraits::RunType; + using ReturnType = ExtractReturnType; + + using RunParamsList = ExtractArgs; + using BoundParamsList = TakeTypeListItem; + using UnboundParamsList = DropTypeListItem; + + using BoundArgsList = TypeList; + + using UnboundRunType = MakeFunctionType; +}; + +template +std::enable_if_t::is_nullable, bool> IsNull( + const Functor& functor) { + return !functor; +} + +template +std::enable_if_t::is_nullable, bool> IsNull( + const Functor&) { + return false; +} + +// Used by ApplyCancellationTraits below. +template +bool ApplyCancellationTraitsImpl(const Functor& functor, + const BoundArgsTuple& bound_args, + std::index_sequence) { + return CallbackCancellationTraits::IsCancelled( + functor, std::get(bound_args)...); +} + +// Relays |base| to corresponding CallbackCancellationTraits<>::Run(). Returns +// true if the callback |base| represents is canceled. +template +bool ApplyCancellationTraits(const BindStateBase* base) { + const BindStateType* storage = static_cast(base); + static constexpr size_t num_bound_args = + std::tuple_sizebound_args_)>::value; + return ApplyCancellationTraitsImpl( + storage->functor_, storage->bound_args_, + std::make_index_sequence()); +}; + +// BindState<> +// +// This stores all the state passed into Bind(). +template +struct BindState final : BindStateBase { + using IsCancellable = std::integral_constant< + bool, + CallbackCancellationTraits>::is_cancellable>; + + template + explicit BindState(BindStateBase::InvokeFuncStorage invoke_func, + ForwardFunctor&& functor, + ForwardBoundArgs&&... bound_args) + // IsCancellable is std::false_type if + // CallbackCancellationTraits<>::IsCancelled returns always false. + // Otherwise, it's std::true_type. + : BindState(IsCancellable{}, + invoke_func, + std::forward(functor), + std::forward(bound_args)...) {} + + Functor functor_; + std::tuple bound_args_; + + private: + template + explicit BindState(std::true_type, + BindStateBase::InvokeFuncStorage invoke_func, + ForwardFunctor&& functor, + ForwardBoundArgs&&... bound_args) + : BindStateBase(invoke_func, + &Destroy, + &ApplyCancellationTraits), + functor_(std::forward(functor)), + bound_args_(std::forward(bound_args)...) { + DCHECK(!IsNull(functor_)); + } + + template + explicit BindState(std::false_type, + BindStateBase::InvokeFuncStorage invoke_func, + ForwardFunctor&& functor, + ForwardBoundArgs&&... bound_args) + : BindStateBase(invoke_func, &Destroy), + functor_(std::forward(functor)), + bound_args_(std::forward(bound_args)...) { + DCHECK(!IsNull(functor_)); + } + + ~BindState() = default; + + static void Destroy(const BindStateBase* self) { + delete static_cast(self); + } +}; + +// Used to implement MakeBindStateType. +template +struct MakeBindStateTypeImpl; + +template +struct MakeBindStateTypeImpl { + static_assert(!HasRefCountedTypeAsRawPtr...>::value, + "A parameter is a refcounted type and needs scoped_refptr."); + using Type = BindState, std::decay_t...>; +}; + +template +struct MakeBindStateTypeImpl { + using Type = BindState>; +}; + +template +struct MakeBindStateTypeImpl { + private: + using DecayedReceiver = std::decay_t; + + static_assert(!std::is_array>::value, + "First bound argument to a method cannot be an array."); + static_assert( + !std::is_pointer::value || + IsRefCountedType>::value, + "Receivers may not be raw pointers. If using a raw pointer here is safe" + " and has no lifetime concerns, use base::Unretained() and document why" + " it's safe."); + static_assert(!HasRefCountedTypeAsRawPtr...>::value, + "A parameter is a refcounted type and needs scoped_refptr."); + + public: + using Type = BindState< + std::decay_t, + std::conditional_t::value, + scoped_refptr>, + DecayedReceiver>, + std::decay_t...>; +}; + +template +using MakeBindStateType = + typename MakeBindStateTypeImpl::is_method, + Functor, + BoundArgs...>::Type; + +} // namespace internal + +// An injection point to control |this| pointer behavior on a method invocation. +// If IsWeakReceiver<> is true_type for |T| and |T| is used for a receiver of a +// method, base::Bind cancels the method invocation if the receiver is tested as +// false. +// E.g. Foo::bar() is not called: +// struct Foo : base::SupportsWeakPtr { +// void bar() {} +// }; +// +// WeakPtr oo = nullptr; +// base::Bind(&Foo::bar, oo).Run(); +template +struct IsWeakReceiver : std::false_type {}; + +template +struct IsWeakReceiver> : IsWeakReceiver {}; + +template +struct IsWeakReceiver> : std::true_type {}; + +// An injection point to control how bound objects passed to the target +// function. BindUnwrapTraits<>::Unwrap() is called for each bound objects right +// before the target function is invoked. +template +struct BindUnwrapTraits { + template + static T&& Unwrap(T&& o) { + return std::forward(o); + } +}; + +template +struct BindUnwrapTraits> { + static T* Unwrap(const internal::UnretainedWrapper& o) { return o.get(); } +}; + +template +struct BindUnwrapTraits> { + static const T& Unwrap(const internal::ConstRefWrapper& o) { + return o.get(); + } +}; + +template +struct BindUnwrapTraits> { + static T* Unwrap(const internal::RetainedRefWrapper& o) { return o.get(); } +}; + +template +struct BindUnwrapTraits> { + static T* Unwrap(const internal::OwnedWrapper& o) { return o.get(); } +}; + +template +struct BindUnwrapTraits> { + static T Unwrap(const internal::PassedWrapper& o) { return o.Take(); } +}; + +// CallbackCancellationTraits allows customization of Callback's cancellation +// semantics. By default, callbacks are not cancellable. A specialization should +// set is_cancellable = true and implement an IsCancelled() that returns if the +// callback should be cancelled. +template +struct CallbackCancellationTraits { + static constexpr bool is_cancellable = false; +}; + +// Specialization for method bound to weak pointer receiver. +template +struct CallbackCancellationTraits< + Functor, + std::tuple, + std::enable_if_t< + internal::IsWeakMethod::is_method, + BoundArgs...>::value>> { + static constexpr bool is_cancellable = true; + + template + static bool IsCancelled(const Functor&, + const Receiver& receiver, + const Args&...) { + return !receiver; + } +}; + +// Specialization for a nested bind. +template +struct CallbackCancellationTraits, + std::tuple> { + static constexpr bool is_cancellable = true; + + template + static bool IsCancelled(const Functor& functor, const BoundArgs&...) { + return functor.IsCancelled(); + } +}; + +template +struct CallbackCancellationTraits, + std::tuple> { + static constexpr bool is_cancellable = true; + + template + static bool IsCancelled(const Functor& functor, const BoundArgs&...) { + return functor.IsCancelled(); + } +}; + +// Returns a RunType of bound functor. +// E.g. MakeUnboundRunType is evaluated to R(C). +template +using MakeUnboundRunType = + typename internal::BindTypeHelper::UnboundRunType; + +} // namespace base + +#endif // BASE_BIND_INTERNAL_H_ diff --git a/native/zucchini/vendor/libchrome/base/callback.h b/native/zucchini/vendor/libchrome/base/callback.h new file mode 100644 index 0000000..bcda5af --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/callback.h @@ -0,0 +1,146 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// NOTE: Header files that do not require the full definition of Callback or +// Closure should #include "base/callback_forward.h" instead of this file. + +#ifndef BASE_CALLBACK_H_ +#define BASE_CALLBACK_H_ + +#include + +#include "base/callback_forward.h" +#include "base/callback_internal.h" + +// ----------------------------------------------------------------------------- +// Usage documentation +// ----------------------------------------------------------------------------- +// +// Overview: +// A callback is similar in concept to a function pointer: it wraps a runnable +// object such as a function, method, lambda, or even another callback, allowing +// the runnable object to be invoked later via the callback object. +// +// Unlike function pointers, callbacks are created with base::BindOnce() or +// base::BindRepeating() and support partial function application. +// +// A base::OnceCallback may be Run() at most once; a base::RepeatingCallback may +// be Run() any number of times. |is_null()| is guaranteed to return true for a +// moved-from callback. +// +// // The lambda takes two arguments, but the first argument |x| is bound at +// // callback creation. +// base::OnceCallback cb = base::BindOnce([] (int x, int y) { +// return x + y; +// }, 1); +// // Run() only needs the remaining unbound argument |y|. +// printf("1 + 2 = %d\n", std::move(cb).Run(2)); // Prints 3 +// printf("cb is null? %s\n", +// cb.is_null() ? "true" : "false"); // Prints true +// std::move(cb).Run(2); // Crashes since |cb| has already run. +// +// Callbacks also support cancellation. A common use is binding the receiver +// object as a WeakPtr. If that weak pointer is invalidated, calling Run() +// will be a no-op. Note that |is_cancelled()| and |is_null()| are distinct: +// simply cancelling a callback will not also make it null. +// +// base::Callback is currently a type alias for base::RepeatingCallback. In the +// future, we expect to flip this to default to base::OnceCallback. +// +// See //docs/callback.md for the full documentation. + +namespace base { + +template +class OnceCallback : public internal::CallbackBase { + public: + using RunType = R(Args...); + using PolymorphicInvoke = R (*)(internal::BindStateBase*, + internal::PassingType...); + + constexpr OnceCallback() = default; + OnceCallback(std::nullptr_t) = delete; + + explicit OnceCallback(internal::BindStateBase* bind_state) + : internal::CallbackBase(bind_state) {} + + OnceCallback(const OnceCallback&) = delete; + OnceCallback& operator=(const OnceCallback&) = delete; + + OnceCallback(OnceCallback&&) noexcept = default; + OnceCallback& operator=(OnceCallback&&) noexcept = default; + + OnceCallback(RepeatingCallback other) + : internal::CallbackBase(std::move(other)) {} + + OnceCallback& operator=(RepeatingCallback other) { + static_cast(*this) = std::move(other); + return *this; + } + + bool Equals(const OnceCallback& other) const { return EqualsInternal(other); } + + R Run(Args... args) const & { + static_assert(!sizeof(*this), + "OnceCallback::Run() may only be invoked on a non-const " + "rvalue, i.e. std::move(callback).Run()."); + NOTREACHED(); + } + + R Run(Args... args) && { + // Move the callback instance into a local variable before the invocation, + // that ensures the internal state is cleared after the invocation. + // It's not safe to touch |this| after the invocation, since running the + // bound function may destroy |this|. + OnceCallback cb = std::move(*this); + PolymorphicInvoke f = + reinterpret_cast(cb.polymorphic_invoke()); + return f(cb.bind_state_.get(), std::forward(args)...); + } +}; + +template +class RepeatingCallback : public internal::CallbackBaseCopyable { + public: + using RunType = R(Args...); + using PolymorphicInvoke = R (*)(internal::BindStateBase*, + internal::PassingType...); + + constexpr RepeatingCallback() = default; + RepeatingCallback(std::nullptr_t) = delete; + + explicit RepeatingCallback(internal::BindStateBase* bind_state) + : internal::CallbackBaseCopyable(bind_state) {} + + // Copyable and movable. + RepeatingCallback(const RepeatingCallback&) = default; + RepeatingCallback& operator=(const RepeatingCallback&) = default; + RepeatingCallback(RepeatingCallback&&) noexcept = default; + RepeatingCallback& operator=(RepeatingCallback&&) noexcept = default; + + bool Equals(const RepeatingCallback& other) const { + return EqualsInternal(other); + } + + R Run(Args... args) const & { + PolymorphicInvoke f = + reinterpret_cast(this->polymorphic_invoke()); + return f(this->bind_state_.get(), std::forward(args)...); + } + + R Run(Args... args) && { + // Move the callback instance into a local variable before the invocation, + // that ensures the internal state is cleared after the invocation. + // It's not safe to touch |this| after the invocation, since running the + // bound function may destroy |this|. + RepeatingCallback cb = std::move(*this); + PolymorphicInvoke f = + reinterpret_cast(cb.polymorphic_invoke()); + return f(cb.bind_state_.get(), std::forward(args)...); + } +}; + +} // namespace base + +#endif // BASE_CALLBACK_H_ diff --git a/native/zucchini/vendor/libchrome/base/callback_forward.h b/native/zucchini/vendor/libchrome/base/callback_forward.h new file mode 100644 index 0000000..f1851c4 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/callback_forward.h @@ -0,0 +1,27 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_CALLBACK_FORWARD_H_ +#define BASE_CALLBACK_FORWARD_H_ + +namespace base { + +template +class OnceCallback; + +template +class RepeatingCallback; + +template +using Callback = RepeatingCallback; + +// Syntactic sugar to make Callback easier to declare since it +// will be used in a lot of APIs with delayed execution. +using OnceClosure = OnceCallback; +using RepeatingClosure = RepeatingCallback; +using Closure = Callback; + +} // namespace base + +#endif // BASE_CALLBACK_FORWARD_H_ diff --git a/native/zucchini/vendor/libchrome/base/callback_helpers.h b/native/zucchini/vendor/libchrome/base/callback_helpers.h new file mode 100644 index 0000000..0cdda6d --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/callback_helpers.h @@ -0,0 +1,104 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This defines helpful methods for dealing with Callbacks. Because Callbacks +// are implemented using templates, with a class per callback signature, adding +// methods to Callback<> itself is unattractive (lots of extra code gets +// generated). Instead, consider adding methods here. + +#ifndef BASE_CALLBACK_HELPERS_H_ +#define BASE_CALLBACK_HELPERS_H_ + +#include + +#include "base/atomicops.h" +#include "base/bind.h" +#include "base/callback.h" +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "base/memory/ptr_util.h" + +namespace base { + +// Prefer std::move() over ResetAndReturn(). +template +CallbackType ResetAndReturn(CallbackType* cb) { + CallbackType ret(std::move(*cb)); + DCHECK(!*cb); + return ret; +} + +namespace internal { + +template +class AdaptCallbackForRepeatingHelper final { + public: + explicit AdaptCallbackForRepeatingHelper(OnceCallback callback) + : callback_(std::move(callback)) { + DCHECK(callback_); + } + + void Run(Args... args) { + if (subtle::NoBarrier_AtomicExchange(&has_run_, 1)) + return; + DCHECK(callback_); + std::move(callback_).Run(std::forward(args)...); + } + + private: + volatile subtle::Atomic32 has_run_ = 0; + base::OnceCallback callback_; + + DISALLOW_COPY_AND_ASSIGN(AdaptCallbackForRepeatingHelper); +}; + +} // namespace internal + +// Wraps the given OnceCallback into a RepeatingCallback that relays its +// invocation to the original OnceCallback on the first invocation. The +// following invocations are just ignored. +// +// Note that this deliberately subverts the Once/Repeating paradigm of Callbacks +// but helps ease the migration from old-style Callbacks. Avoid if possible; use +// if necessary for migration. TODO(tzik): Remove it. https://crbug.com/730593 +template +RepeatingCallback AdaptCallbackForRepeating( + OnceCallback callback) { + using Helper = internal::AdaptCallbackForRepeatingHelper; + return base::BindRepeating(&Helper::Run, + std::make_unique(std::move(callback))); +} + +// ScopedClosureRunner is akin to std::unique_ptr<> for Closures. It ensures +// that the Closure is executed no matter how the current scope exits. +class BASE_EXPORT ScopedClosureRunner { + public: + ScopedClosureRunner(); + explicit ScopedClosureRunner(OnceClosure closure); + ~ScopedClosureRunner(); + + ScopedClosureRunner(ScopedClosureRunner&& other); + + // Releases the current closure if it's set and replaces it with the closure + // from |other|. + ScopedClosureRunner& operator=(ScopedClosureRunner&& other); + + // Calls the current closure and resets it, so it wont be called again. + void RunAndReset(); + + // Replaces closure with the new one releasing the old one without calling it. + void ReplaceClosure(OnceClosure closure); + + // Releases the Closure without calling. + OnceClosure Release() WARN_UNUSED_RESULT; + + private: + OnceClosure closure_; + + DISALLOW_COPY_AND_ASSIGN(ScopedClosureRunner); +}; + +} // namespace base + +#endif // BASE_CALLBACK_HELPERS_H_ diff --git a/native/zucchini/vendor/libchrome/base/callback_internal.cc b/native/zucchini/vendor/libchrome/base/callback_internal.cc new file mode 100644 index 0000000..dd000ca --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/callback_internal.cc @@ -0,0 +1,84 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/callback_internal.h" + +#include "base/logging.h" + +namespace base { +namespace internal { + +namespace { + +bool ReturnFalse(const BindStateBase*) { + return false; +} + +} // namespace + +void BindStateBaseRefCountTraits::Destruct(const BindStateBase* bind_state) { + bind_state->destructor_(bind_state); +} + +BindStateBase::BindStateBase(InvokeFuncStorage polymorphic_invoke, + void (*destructor)(const BindStateBase*)) + : BindStateBase(polymorphic_invoke, destructor, &ReturnFalse) { +} + +BindStateBase::BindStateBase(InvokeFuncStorage polymorphic_invoke, + void (*destructor)(const BindStateBase*), + bool (*is_cancelled)(const BindStateBase*)) + : polymorphic_invoke_(polymorphic_invoke), + destructor_(destructor), + is_cancelled_(is_cancelled) {} + +CallbackBase& CallbackBase::operator=(CallbackBase&& c) noexcept = default; +CallbackBase::CallbackBase(const CallbackBaseCopyable& c) + : bind_state_(c.bind_state_) {} + +CallbackBase& CallbackBase::operator=(const CallbackBaseCopyable& c) { + bind_state_ = c.bind_state_; + return *this; +} + +CallbackBase::CallbackBase(CallbackBaseCopyable&& c) noexcept + : bind_state_(std::move(c.bind_state_)) {} + +CallbackBase& CallbackBase::operator=(CallbackBaseCopyable&& c) noexcept { + bind_state_ = std::move(c.bind_state_); + return *this; +} + +void CallbackBase::Reset() { + // NULL the bind_state_ last, since it may be holding the last ref to whatever + // object owns us, and we may be deleted after that. + bind_state_ = nullptr; +} + +bool CallbackBase::IsCancelled() const { + DCHECK(bind_state_); + return bind_state_->IsCancelled(); +} + +bool CallbackBase::EqualsInternal(const CallbackBase& other) const { + return bind_state_ == other.bind_state_; +} + +CallbackBase::~CallbackBase() = default; + +CallbackBaseCopyable::CallbackBaseCopyable(const CallbackBaseCopyable& c) { + bind_state_ = c.bind_state_; +} + +CallbackBaseCopyable& CallbackBaseCopyable::operator=( + const CallbackBaseCopyable& c) { + bind_state_ = c.bind_state_; + return *this; +} + +CallbackBaseCopyable& CallbackBaseCopyable::operator=( + CallbackBaseCopyable&& c) noexcept = default; + +} // namespace internal +} // namespace base diff --git a/native/zucchini/vendor/libchrome/base/callback_internal.h b/native/zucchini/vendor/libchrome/base/callback_internal.h new file mode 100644 index 0000000..1215e3e --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/callback_internal.h @@ -0,0 +1,165 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file contains utility functions and classes that help the +// implementation, and management of the Callback objects. + +#ifndef BASE_CALLBACK_INTERNAL_H_ +#define BASE_CALLBACK_INTERNAL_H_ + +#include "base/base_export.h" +#include "base/callback_forward.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" + +namespace base { + +struct FakeBindState; + +namespace internal { + +class CallbackBase; +class CallbackBaseCopyable; + +class BindStateBase; + +template +struct BindState; + +struct BindStateBaseRefCountTraits { + static void Destruct(const BindStateBase*); +}; + +template +using PassingType = std::conditional_t::value, T, T&&>; + +// BindStateBase is used to provide an opaque handle that the Callback +// class can use to represent a function object with bound arguments. It +// behaves as an existential type that is used by a corresponding +// DoInvoke function to perform the function execution. This allows +// us to shield the Callback class from the types of the bound argument via +// "type erasure." +// At the base level, the only task is to add reference counting data. Don't use +// RefCountedThreadSafe since it requires the destructor to be a virtual method. +// Creating a vtable for every BindState template instantiation results in a lot +// of bloat. Its only task is to call the destructor which can be done with a +// function pointer. +class BASE_EXPORT BindStateBase + : public RefCountedThreadSafe { + public: + REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE(); + + using InvokeFuncStorage = void(*)(); + + private: + BindStateBase(InvokeFuncStorage polymorphic_invoke, + void (*destructor)(const BindStateBase*)); + BindStateBase(InvokeFuncStorage polymorphic_invoke, + void (*destructor)(const BindStateBase*), + bool (*is_cancelled)(const BindStateBase*)); + + ~BindStateBase() = default; + + friend struct BindStateBaseRefCountTraits; + friend class RefCountedThreadSafe; + + friend class CallbackBase; + friend class CallbackBaseCopyable; + + // Whitelist subclasses that access the destructor of BindStateBase. + template + friend struct BindState; + friend struct ::base::FakeBindState; + + bool IsCancelled() const { + return is_cancelled_(this); + } + + // In C++, it is safe to cast function pointers to function pointers of + // another type. It is not okay to use void*. We create a InvokeFuncStorage + // that that can store our function pointer, and then cast it back to + // the original type on usage. + InvokeFuncStorage polymorphic_invoke_; + + // Pointer to a function that will properly destroy |this|. + void (*destructor_)(const BindStateBase*); + bool (*is_cancelled_)(const BindStateBase*); + + DISALLOW_COPY_AND_ASSIGN(BindStateBase); +}; + +// Holds the Callback methods that don't require specialization to reduce +// template bloat. +// CallbackBase is a direct base class of MoveOnly callbacks, and +// CallbackBase uses CallbackBase for its implementation. +class BASE_EXPORT CallbackBase { + public: + inline CallbackBase(CallbackBase&& c) noexcept; + CallbackBase& operator=(CallbackBase&& c) noexcept; + + explicit CallbackBase(const CallbackBaseCopyable& c); + CallbackBase& operator=(const CallbackBaseCopyable& c); + + explicit CallbackBase(CallbackBaseCopyable&& c) noexcept; + CallbackBase& operator=(CallbackBaseCopyable&& c) noexcept; + + // Returns true if Callback is null (doesn't refer to anything). + bool is_null() const { return !bind_state_; } + explicit operator bool() const { return !is_null(); } + + // Returns true if the callback invocation will be nop due to an cancellation. + // It's invalid to call this on uninitialized callback. + bool IsCancelled() const; + + // Returns the Callback into an uninitialized state. + void Reset(); + + protected: + using InvokeFuncStorage = BindStateBase::InvokeFuncStorage; + + // Returns true if this callback equals |other|. |other| may be null. + bool EqualsInternal(const CallbackBase& other) const; + + constexpr inline CallbackBase(); + + // Allow initializing of |bind_state_| via the constructor to avoid default + // initialization of the scoped_refptr. + explicit inline CallbackBase(BindStateBase* bind_state); + + InvokeFuncStorage polymorphic_invoke() const { + return bind_state_->polymorphic_invoke_; + } + + // Force the destructor to be instantiated inside this translation unit so + // that our subclasses will not get inlined versions. Avoids more template + // bloat. + ~CallbackBase(); + + scoped_refptr bind_state_; +}; + +constexpr CallbackBase::CallbackBase() = default; +CallbackBase::CallbackBase(CallbackBase&&) noexcept = default; +CallbackBase::CallbackBase(BindStateBase* bind_state) + : bind_state_(AdoptRef(bind_state)) {} + +// CallbackBase is a direct base class of Copyable Callbacks. +class BASE_EXPORT CallbackBaseCopyable : public CallbackBase { + public: + CallbackBaseCopyable(const CallbackBaseCopyable& c); + CallbackBaseCopyable(CallbackBaseCopyable&& c) noexcept = default; + CallbackBaseCopyable& operator=(const CallbackBaseCopyable& c); + CallbackBaseCopyable& operator=(CallbackBaseCopyable&& c) noexcept; + + protected: + constexpr CallbackBaseCopyable() = default; + explicit CallbackBaseCopyable(BindStateBase* bind_state) + : CallbackBase(bind_state) {} + ~CallbackBaseCopyable() = default; +}; + +} // namespace internal +} // namespace base + +#endif // BASE_CALLBACK_INTERNAL_H_ diff --git a/native/zucchini/vendor/libchrome/base/cfi_buildflags.h b/native/zucchini/vendor/libchrome/base/cfi_buildflags.h new file mode 100644 index 0000000..be8270e --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/cfi_buildflags.h @@ -0,0 +1,7 @@ +// Generated by build/write_buildflag_header.py +// From "base_debugging_flags" +#ifndef BASE_CFI_BUILDFLAGS_H_ +#define BASE_CFI_BUILDFLAGS_H_ +#include "build/buildflag.h" +#define BUILDFLAG_INTERNAL_CFI_ICALL_CHECK() (0) +#endif // BASE_CFI_BUILDFLAGS_H_ diff --git a/native/zucchini/vendor/libchrome/base/command_line.h b/native/zucchini/vendor/libchrome/base/command_line.h new file mode 100644 index 0000000..25fd7d9 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/command_line.h @@ -0,0 +1,247 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This class works with command lines: building and parsing. +// Arguments with prefixes ('--', '-', and on Windows, '/') are switches. +// Switches will precede all other arguments without switch prefixes. +// Switches can optionally have values, delimited by '=', e.g., "-switch=value". +// An argument of "--" will terminate switch parsing during initialization, +// interpreting subsequent tokens as non-switch arguments, regardless of prefix. + +// There is a singleton read-only CommandLine that represents the command line +// that the current process was started with. It must be initialized in main(). + +#ifndef BASE_COMMAND_LINE_H_ +#define BASE_COMMAND_LINE_H_ + +#include +#include +#include +#include + +#include "base/base_export.h" +#include "base/strings/string16.h" +#include "base/strings/string_piece.h" +#include "build/build_config.h" + +namespace base { + +class FilePath; + +class BASE_EXPORT CommandLine { + public: +#if defined(OS_WIN) + // The native command line string type. + using StringType = string16; +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + using StringType = std::string; +#endif + + using CharType = StringType::value_type; + using StringVector = std::vector; + using SwitchMap = std::map>; + + // A constructor for CommandLines that only carry switches and arguments. + enum NoProgram { NO_PROGRAM }; + explicit CommandLine(NoProgram no_program); + + // Construct a new command line with |program| as argv[0]. + explicit CommandLine(const FilePath& program); + + // Construct a new command line from an argument list. + CommandLine(int argc, const CharType* const* argv); + explicit CommandLine(const StringVector& argv); + + // Override copy and assign to ensure |switches_by_stringpiece_| is valid. + CommandLine(const CommandLine& other); + CommandLine& operator=(const CommandLine& other); + + ~CommandLine(); + +#if defined(OS_WIN) + // By default this class will treat command-line arguments beginning with + // slashes as switches on Windows, but not other platforms. + // + // If this behavior is inappropriate for your application, you can call this + // function BEFORE initializing the current process' global command line + // object and the behavior will be the same as Posix systems (only hyphens + // begin switches, everything else will be an arg). + static void set_slash_is_not_a_switch(); + + // Normally when the CommandLine singleton is initialized it gets the command + // line via the GetCommandLineW API and then uses the shell32 API + // CommandLineToArgvW to parse the command line and convert it back to + // argc and argv. Tests who don't want this dependency on shell32 and need + // to honor the arguments passed in should use this function. + static void InitUsingArgvForTesting(int argc, const char* const* argv); +#endif + + // Initialize the current process CommandLine singleton. On Windows, ignores + // its arguments (we instead parse GetCommandLineW() directly) because we + // don't trust the CRT's parsing of the command line, but it still must be + // called to set up the command line. Returns false if initialization has + // already occurred, and true otherwise. Only the caller receiving a 'true' + // return value should take responsibility for calling Reset. + static bool Init(int argc, const char* const* argv); + + // Destroys the current process CommandLine singleton. This is necessary if + // you want to reset the base library to its initial state (for example, in an + // outer library that needs to be able to terminate, and be re-initialized). + // If Init is called only once, as in main(), Reset() is not necessary. + // Do not call this in tests. Use base::test::ScopedCommandLine instead. + static void Reset(); + + // Get the singleton CommandLine representing the current process's + // command line. Note: returned value is mutable, but not thread safe; + // only mutate if you know what you're doing! + static CommandLine* ForCurrentProcess(); + + // Returns true if the CommandLine has been initialized for the given process. + static bool InitializedForCurrentProcess(); + +#if defined(OS_WIN) + static CommandLine FromString(const string16& command_line); +#endif + + // Initialize from an argv vector. + void InitFromArgv(int argc, const CharType* const* argv); + void InitFromArgv(const StringVector& argv); + + // Constructs and returns the represented command line string. + // CAUTION! This should be avoided on POSIX because quoting behavior is + // unclear. + StringType GetCommandLineString() const { + return GetCommandLineStringInternal(false); + } + +#if defined(OS_WIN) + // Constructs and returns the represented command line string. Assumes the + // command line contains placeholders (eg, %1) and quotes any program or + // argument with a '%' in it. This should be avoided unless the placeholder is + // required by an external interface (eg, the Windows registry), because it is + // not generally safe to replace it with an arbitrary string. If possible, + // placeholders should be replaced *before* converting the command line to a + // string. + StringType GetCommandLineStringWithPlaceholders() const { + return GetCommandLineStringInternal(true); + } +#endif + + // Constructs and returns the represented arguments string. + // CAUTION! This should be avoided on POSIX because quoting behavior is + // unclear. + StringType GetArgumentsString() const { + return GetArgumentsStringInternal(false); + } + +#if defined(OS_WIN) + // Constructs and returns the represented arguments string. Assumes the + // command line contains placeholders (eg, %1) and quotes any argument with a + // '%' in it. This should be avoided unless the placeholder is required by an + // external interface (eg, the Windows registry), because it is not generally + // safe to replace it with an arbitrary string. If possible, placeholders + // should be replaced *before* converting the arguments to a string. + StringType GetArgumentsStringWithPlaceholders() const { + return GetArgumentsStringInternal(true); + } +#endif + + // Returns the original command line string as a vector of strings. + const StringVector& argv() const { return argv_; } + + // Get and Set the program part of the command line string (the first item). + FilePath GetProgram() const; + void SetProgram(const FilePath& program); + + // Returns true if this command line contains the given switch. + // Switch names must be lowercase. + // The second override provides an optimized version to avoid inlining codegen + // at every callsite to find the length of the constant and construct a + // StringPiece. + bool HasSwitch(const StringPiece& switch_string) const; + bool HasSwitch(const char switch_constant[]) const; + + // Returns the value associated with the given switch. If the switch has no + // value or isn't present, this method returns the empty string. + // Switch names must be lowercase. + std::string GetSwitchValueASCII(const StringPiece& switch_string) const; + FilePath GetSwitchValuePath(const StringPiece& switch_string) const; + StringType GetSwitchValueNative(const StringPiece& switch_string) const; + + // Get a copy of all switches, along with their values. + const SwitchMap& GetSwitches() const { return switches_; } + + // Append a switch [with optional value] to the command line. + // Note: Switches will precede arguments regardless of appending order. + void AppendSwitch(const std::string& switch_string); + void AppendSwitchPath(const std::string& switch_string, + const FilePath& path); + void AppendSwitchNative(const std::string& switch_string, + const StringType& value); + void AppendSwitchASCII(const std::string& switch_string, + const std::string& value); + + // Copy a set of switches (and any values) from another command line. + // Commonly used when launching a subprocess. + void CopySwitchesFrom(const CommandLine& source, + const char* const switches[], + size_t count); + + // Get the remaining arguments to the command. + StringVector GetArgs() const; + + // Append an argument to the command line. Note that the argument is quoted + // properly such that it is interpreted as one argument to the target command. + // AppendArg is primarily for ASCII; non-ASCII input is interpreted as UTF-8. + // Note: Switches will precede arguments regardless of appending order. + void AppendArg(const std::string& value); + void AppendArgPath(const FilePath& value); + void AppendArgNative(const StringType& value); + + // Append the switches and arguments from another command line to this one. + // If |include_program| is true, include |other|'s program as well. + void AppendArguments(const CommandLine& other, bool include_program); + + // Insert a command before the current command. + // Common for debuggers, like "gdb --args". + void PrependWrapper(const StringType& wrapper); + +#if defined(OS_WIN) + // Initialize by parsing the given command line string. + // The program name is assumed to be the first item in the string. + void ParseFromString(const string16& command_line); +#endif + + private: + // Disallow default constructor; a program name must be explicitly specified. + CommandLine() = delete; + // Allow the copy constructor. A common pattern is to copy of the current + // process's command line and then add some flags to it. For example: + // CommandLine cl(*CommandLine::ForCurrentProcess()); + // cl.AppendSwitch(...); + + // Internal version of GetCommandLineString. If |quote_placeholders| is true, + // also quotes parts with '%' in them. + StringType GetCommandLineStringInternal(bool quote_placeholders) const; + + // Internal version of GetArgumentsString. If |quote_placeholders| is true, + // also quotes parts with '%' in them. + StringType GetArgumentsStringInternal(bool quote_placeholders) const; + + // The singleton CommandLine representing the current process's command line. + static CommandLine* current_process_commandline_; + + // The argv array: { program, [(--|-|/)switch[=value]]*, [--], [argument]* } + StringVector argv_; + + // Parsed-out switch keys and values. + SwitchMap switches_; + + // The index after the program and switches, any arguments start here. + size_t begin_args_; +}; + +} // namespace base + +#endif // BASE_COMMAND_LINE_H_ diff --git a/native/zucchini/vendor/libchrome/base/compiler_specific.h b/native/zucchini/vendor/libchrome/base/compiler_specific.h new file mode 100644 index 0000000..a930f5d --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/compiler_specific.h @@ -0,0 +1,236 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_COMPILER_SPECIFIC_H_ +#define BASE_COMPILER_SPECIFIC_H_ + +#include "build/build_config.h" + +#if defined(ANDROID) +// Prefer Android's libbase definitions to our own. +#include +#endif // defined(ANDROID) + +#if defined(COMPILER_MSVC) + +// For _Printf_format_string_. +#include + +// Macros for suppressing and disabling warnings on MSVC. +// +// Warning numbers are enumerated at: +// http://msdn.microsoft.com/en-us/library/8x5x43k7(VS.80).aspx +// +// The warning pragma: +// http://msdn.microsoft.com/en-us/library/2c8f766e(VS.80).aspx +// +// Using __pragma instead of #pragma inside macros: +// http://msdn.microsoft.com/en-us/library/d9x1s805.aspx + +// MSVC_SUPPRESS_WARNING disables warning |n| for the remainder of the line and +// for the next line of the source file. +#define MSVC_SUPPRESS_WARNING(n) __pragma(warning(suppress:n)) + +// MSVC_PUSH_DISABLE_WARNING pushes |n| onto a stack of warnings to be disabled. +// The warning remains disabled until popped by MSVC_POP_WARNING. +#define MSVC_PUSH_DISABLE_WARNING(n) __pragma(warning(push)) \ + __pragma(warning(disable:n)) + +// MSVC_PUSH_WARNING_LEVEL pushes |n| as the global warning level. The level +// remains in effect until popped by MSVC_POP_WARNING(). Use 0 to disable all +// warnings. +#define MSVC_PUSH_WARNING_LEVEL(n) __pragma(warning(push, n)) + +// Pop effects of innermost MSVC_PUSH_* macro. +#define MSVC_POP_WARNING() __pragma(warning(pop)) + +#define MSVC_DISABLE_OPTIMIZE() __pragma(optimize("", off)) +#define MSVC_ENABLE_OPTIMIZE() __pragma(optimize("", on)) + +#else // Not MSVC + +#define _Printf_format_string_ +#define MSVC_SUPPRESS_WARNING(n) +#define MSVC_PUSH_DISABLE_WARNING(n) +#define MSVC_PUSH_WARNING_LEVEL(n) +#define MSVC_POP_WARNING() +#define MSVC_DISABLE_OPTIMIZE() +#define MSVC_ENABLE_OPTIMIZE() + +#endif // COMPILER_MSVC + +// Annotate a variable indicating it's ok if the variable is not used. +// (Typically used to silence a compiler warning when the assignment +// is important for some other reason.) +// Use like: +// int x = ...; +// ALLOW_UNUSED_LOCAL(x); +#define ALLOW_UNUSED_LOCAL(x) (void)x + +// Annotate a typedef or function indicating it's ok if it's not used. +// Use like: +// typedef Foo Bar ALLOW_UNUSED_TYPE; +#if defined(COMPILER_GCC) || defined(__clang__) +#define ALLOW_UNUSED_TYPE __attribute__((unused)) +#else +#define ALLOW_UNUSED_TYPE +#endif + +// Annotate a function indicating it should not be inlined. +// Use like: +// NOINLINE void DoStuff() { ... } +#if defined(COMPILER_GCC) +#define NOINLINE __attribute__((noinline)) +#elif defined(COMPILER_MSVC) +#define NOINLINE __declspec(noinline) +#else +#define NOINLINE +#endif + +#if COMPILER_GCC && defined(NDEBUG) +#define ALWAYS_INLINE inline __attribute__((__always_inline__)) +#elif COMPILER_MSVC && defined(NDEBUG) +#define ALWAYS_INLINE __forceinline +#else +#define ALWAYS_INLINE inline +#endif + +// Specify memory alignment for structs, classes, etc. +// Use like: +// class ALIGNAS(16) MyClass { ... } +// ALIGNAS(16) int array[4]; +// +// In most places you can use the C++11 keyword "alignas", which is preferred. +// +// But compilers have trouble mixing __attribute__((...)) syntax with +// alignas(...) syntax. +// +// Doesn't work in clang or gcc: +// struct alignas(16) __attribute__((packed)) S { char c; }; +// Works in clang but not gcc: +// struct __attribute__((packed)) alignas(16) S2 { char c; }; +// Works in clang and gcc: +// struct alignas(16) S3 { char c; } __attribute__((packed)); +// +// There are also some attributes that must be specified *before* a class +// definition: visibility (used for exporting functions/classes) is one of +// these attributes. This means that it is not possible to use alignas() with a +// class that is marked as exported. +#if defined(COMPILER_MSVC) +#define ALIGNAS(byte_alignment) __declspec(align(byte_alignment)) +#elif defined(COMPILER_GCC) +#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment))) +#endif + +// Annotate a function indicating the caller must examine the return value. +// Use like: +// int foo() WARN_UNUSED_RESULT; +// To explicitly ignore a result, see |ignore_result()| in base/macros.h. +#undef WARN_UNUSED_RESULT +#if defined(COMPILER_GCC) || defined(__clang__) +#define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#else +#define WARN_UNUSED_RESULT +#endif + +// Tell the compiler a function is using a printf-style format string. +// |format_param| is the one-based index of the format string parameter; +// |dots_param| is the one-based index of the "..." parameter. +// For v*printf functions (which take a va_list), pass 0 for dots_param. +// (This is undocumented but matches what the system C headers do.) +#if defined(COMPILER_GCC) || defined(__clang__) +#define PRINTF_FORMAT(format_param, dots_param) \ + __attribute__((format(printf, format_param, dots_param))) +#else +#define PRINTF_FORMAT(format_param, dots_param) +#endif + +// WPRINTF_FORMAT is the same, but for wide format strings. +// This doesn't appear to yet be implemented in any compiler. +// See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=38308 . +#define WPRINTF_FORMAT(format_param, dots_param) +// If available, it would look like: +// __attribute__((format(wprintf, format_param, dots_param))) + +// Sanitizers annotations. +#if defined(__has_attribute) +#if __has_attribute(no_sanitize) +#define NO_SANITIZE(what) __attribute__((no_sanitize(what))) +#endif +#endif +#if !defined(NO_SANITIZE) +#define NO_SANITIZE(what) +#endif + +// MemorySanitizer annotations. +#if defined(MEMORY_SANITIZER) && !defined(OS_NACL) +#include + +// Mark a memory region fully initialized. +// Use this to annotate code that deliberately reads uninitialized data, for +// example a GC scavenging root set pointers from the stack. +#define MSAN_UNPOISON(p, size) __msan_unpoison(p, size) + +// Check a memory region for initializedness, as if it was being used here. +// If any bits are uninitialized, crash with an MSan report. +// Use this to sanitize data which MSan won't be able to track, e.g. before +// passing data to another process via shared memory. +#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size) \ + __msan_check_mem_is_initialized(p, size) +#else // MEMORY_SANITIZER +#define MSAN_UNPOISON(p, size) +#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size) +#endif // MEMORY_SANITIZER + +// DISABLE_CFI_PERF -- Disable Control Flow Integrity for perf reasons. +#if !defined(DISABLE_CFI_PERF) +#if defined(__clang__) && defined(OFFICIAL_BUILD) +#define DISABLE_CFI_PERF __attribute__((no_sanitize("cfi"))) +#else +#define DISABLE_CFI_PERF +#endif +#endif + +// Macro useful for writing cross-platform function pointers. +#if !defined(CDECL) +#if defined(OS_WIN) +#define CDECL __cdecl +#else // defined(OS_WIN) +#define CDECL +#endif // defined(OS_WIN) +#endif // !defined(CDECL) + +// Macro for hinting that an expression is likely to be false. +#if !defined(UNLIKELY) +#if defined(COMPILER_GCC) || defined(__clang__) +#define UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define UNLIKELY(x) (x) +#endif // defined(COMPILER_GCC) +#endif // !defined(UNLIKELY) + +#if !defined(LIKELY) +#if defined(COMPILER_GCC) || defined(__clang__) +#define LIKELY(x) __builtin_expect(!!(x), 1) +#else +#define LIKELY(x) (x) +#endif // defined(COMPILER_GCC) +#endif // !defined(LIKELY) + +// Compiler feature-detection. +// clang.llvm.org/docs/LanguageExtensions.html#has-feature-and-has-extension +#if defined(__has_feature) +#define HAS_FEATURE(FEATURE) __has_feature(FEATURE) +#else +#define HAS_FEATURE(FEATURE) 0 +#endif + +// Macro for telling -Wimplicit-fallthrough that a fallthrough is intentional. +#if defined(__clang__) +#define FALLTHROUGH [[clang::fallthrough]] +#else +#define FALLTHROUGH +#endif + +#endif // BASE_COMPILER_SPECIFIC_H_ diff --git a/native/zucchini/vendor/libchrome/base/containers/circular_deque.h b/native/zucchini/vendor/libchrome/base/containers/circular_deque.h new file mode 100644 index 0000000..bf42a95 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/containers/circular_deque.h @@ -0,0 +1,1111 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_CONTAINERS_CIRCULAR_DEQUE_H_ +#define BASE_CONTAINERS_CIRCULAR_DEQUE_H_ + +#include +#include +#include +#include +#include + +#include "base/containers/vector_buffer.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/template_util.h" + +// base::circular_deque is similar to std::deque. Unlike std::deque, the +// storage is provided in a flat circular buffer conceptually similar to a +// vector. The beginning and end will wrap around as necessary so that +// pushes and pops will be constant time as long as a capacity expansion is +// not required. +// +// The API should be identical to std::deque with the following differences: +// +// - ITERATORS ARE NOT STABLE. Mutating the container will invalidate all +// iterators. +// +// - Insertions may resize the vector and so are not constant time (std::deque +// guarantees constant time for insertions at the ends). +// +// - Container-wide comparisons are not implemented. If you want to compare +// two containers, use an algorithm so the expensive iteration is explicit. +// +// If you want a similar container with only a queue API, use base::queue in +// base/containers/queue.h. +// +// Constructors: +// circular_deque(); +// circular_deque(size_t count); +// circular_deque(size_t count, const T& value); +// circular_deque(InputIterator first, InputIterator last); +// circular_deque(const circular_deque&); +// circular_deque(circular_deque&&); +// circular_deque(std::initializer_list); +// +// Assignment functions: +// circular_deque& operator=(const circular_deque&); +// circular_deque& operator=(circular_deque&&); +// circular_deque& operator=(std::initializer_list); +// void assign(size_t count, const T& value); +// void assign(InputIterator first, InputIterator last); +// void assign(std::initializer_list value); +// +// Random accessors: +// T& at(size_t); +// const T& at(size_t) const; +// T& operator[](size_t); +// const T& operator[](size_t) const; +// +// End accessors: +// T& front(); +// const T& front() const; +// T& back(); +// const T& back() const; +// +// Iterator functions: +// iterator begin(); +// const_iterator begin() const; +// const_iterator cbegin() const; +// iterator end(); +// const_iterator end() const; +// const_iterator cend() const; +// reverse_iterator rbegin(); +// const_reverse_iterator rbegin() const; +// const_reverse_iterator crbegin() const; +// reverse_iterator rend(); +// const_reverse_iterator rend() const; +// const_reverse_iterator crend() const; +// +// Memory management: +// void reserve(size_t); // SEE IMPLEMENTATION FOR SOME GOTCHAS. +// size_t capacity() const; +// void shrink_to_fit(); +// +// Size management: +// void clear(); +// bool empty() const; +// size_t size() const; +// void resize(size_t); +// void resize(size_t count, const T& value); +// +// Positional insert and erase: +// void insert(const_iterator pos, size_type count, const T& value); +// void insert(const_iterator pos, +// InputIterator first, InputIterator last); +// iterator insert(const_iterator pos, const T& value); +// iterator insert(const_iterator pos, T&& value); +// iterator emplace(const_iterator pos, Args&&... args); +// iterator erase(const_iterator pos); +// iterator erase(const_iterator first, const_iterator last); +// +// End insert and erase: +// void push_front(const T&); +// void push_front(T&&); +// void push_back(const T&); +// void push_back(T&&); +// T& emplace_front(Args&&...); +// T& emplace_back(Args&&...); +// void pop_front(); +// void pop_back(); +// +// General: +// void swap(circular_deque&); + +namespace base { + +template +class circular_deque; + +namespace internal { + +// Start allocating nonempty buffers with this many entries. This is the +// external capacity so the internal buffer will be one larger (= 4) which is +// more even for the allocator. See the descriptions of internal vs. external +// capacity on the comment above the buffer_ variable below. +constexpr size_t kCircularBufferInitialCapacity = 3; + +template +class circular_deque_const_iterator { + public: + using difference_type = std::ptrdiff_t; + using value_type = T; + using pointer = const T*; + using reference = const T&; + using iterator_category = std::random_access_iterator_tag; + + circular_deque_const_iterator() : parent_deque_(nullptr), index_(0) { +#if DCHECK_IS_ON() + created_generation_ = 0; +#endif // DCHECK_IS_ON() + } + + // Dereferencing. + const T& operator*() const { + CheckUnstableUsage(); + parent_deque_->CheckValidIndex(index_); + return parent_deque_->buffer_[index_]; + } + const T* operator->() const { + CheckUnstableUsage(); + parent_deque_->CheckValidIndex(index_); + return &parent_deque_->buffer_[index_]; + } + const value_type& operator[](difference_type i) const { return *(*this + i); } + + // Increment and decrement. + circular_deque_const_iterator& operator++() { + Increment(); + return *this; + } + circular_deque_const_iterator operator++(int) { + circular_deque_const_iterator ret = *this; + Increment(); + return ret; + } + circular_deque_const_iterator& operator--() { + Decrement(); + return *this; + } + circular_deque_const_iterator operator--(int) { + circular_deque_const_iterator ret = *this; + Decrement(); + return ret; + } + + // Random access mutation. + friend circular_deque_const_iterator operator+( + const circular_deque_const_iterator& iter, + difference_type offset) { + circular_deque_const_iterator ret = iter; + ret.Add(offset); + return ret; + } + circular_deque_const_iterator& operator+=(difference_type offset) { + Add(offset); + return *this; + } + friend circular_deque_const_iterator operator-( + const circular_deque_const_iterator& iter, + difference_type offset) { + circular_deque_const_iterator ret = iter; + ret.Add(-offset); + return ret; + } + circular_deque_const_iterator& operator-=(difference_type offset) { + Add(-offset); + return *this; + } + + friend std::ptrdiff_t operator-(const circular_deque_const_iterator& lhs, + const circular_deque_const_iterator& rhs) { + lhs.CheckComparable(rhs); + return lhs.OffsetFromBegin() - rhs.OffsetFromBegin(); + } + + // Comparisons. + friend bool operator==(const circular_deque_const_iterator& lhs, + const circular_deque_const_iterator& rhs) { + lhs.CheckComparable(rhs); + return lhs.index_ == rhs.index_; + } + friend bool operator!=(const circular_deque_const_iterator& lhs, + const circular_deque_const_iterator& rhs) { + return !(lhs == rhs); + } + friend bool operator<(const circular_deque_const_iterator& lhs, + const circular_deque_const_iterator& rhs) { + lhs.CheckComparable(rhs); + return lhs.OffsetFromBegin() < rhs.OffsetFromBegin(); + } + friend bool operator<=(const circular_deque_const_iterator& lhs, + const circular_deque_const_iterator& rhs) { + return !(lhs > rhs); + } + friend bool operator>(const circular_deque_const_iterator& lhs, + const circular_deque_const_iterator& rhs) { + lhs.CheckComparable(rhs); + return lhs.OffsetFromBegin() > rhs.OffsetFromBegin(); + } + friend bool operator>=(const circular_deque_const_iterator& lhs, + const circular_deque_const_iterator& rhs) { + return !(lhs < rhs); + } + + protected: + friend class circular_deque; + + circular_deque_const_iterator(const circular_deque* parent, size_t index) + : parent_deque_(parent), index_(index) { +#if DCHECK_IS_ON() + created_generation_ = parent->generation_; +#endif // DCHECK_IS_ON() + } + + // Returns the offset from the beginning index of the buffer to the current + // item. + size_t OffsetFromBegin() const { + if (index_ >= parent_deque_->begin_) + return index_ - parent_deque_->begin_; // On the same side as begin. + return parent_deque_->buffer_.capacity() - parent_deque_->begin_ + index_; + } + + // Most uses will be ++ and -- so use a simplified implementation. + void Increment() { + CheckUnstableUsage(); + parent_deque_->CheckValidIndex(index_); + index_++; + if (index_ == parent_deque_->buffer_.capacity()) + index_ = 0; + } + void Decrement() { + CheckUnstableUsage(); + parent_deque_->CheckValidIndexOrEnd(index_); + if (index_ == 0) + index_ = parent_deque_->buffer_.capacity() - 1; + else + index_--; + } + void Add(difference_type delta) { + CheckUnstableUsage(); +#if DCHECK_IS_ON() + if (delta <= 0) + parent_deque_->CheckValidIndexOrEnd(index_); + else + parent_deque_->CheckValidIndex(index_); +#endif + // It should be valid to add 0 to any iterator, even if the container is + // empty and the iterator points to end(). The modulo below will divide + // by 0 if the buffer capacity is empty, so it's important to check for + // this case explicitly. + if (delta == 0) + return; + + difference_type new_offset = OffsetFromBegin() + delta; + DCHECK(new_offset >= 0 && + new_offset <= static_cast(parent_deque_->size())); + index_ = (new_offset + parent_deque_->begin_) % + parent_deque_->buffer_.capacity(); + } + +#if DCHECK_IS_ON() + void CheckUnstableUsage() const { + DCHECK(parent_deque_); + // Since circular_deque doesn't guarantee stability, any attempt to + // dereference this iterator after a mutation (i.e. the generation doesn't + // match the original) in the container is illegal. + DCHECK_EQ(created_generation_, parent_deque_->generation_) + << "circular_deque iterator dereferenced after mutation."; + } + void CheckComparable(const circular_deque_const_iterator& other) const { + DCHECK_EQ(parent_deque_, other.parent_deque_); + // Since circular_deque doesn't guarantee stability, two iterators that + // are compared must have been generated without mutating the container. + // If this fires, the container was mutated between generating the two + // iterators being compared. + DCHECK_EQ(created_generation_, other.created_generation_); + } +#else + inline void CheckUnstableUsage() const {} + inline void CheckComparable(const circular_deque_const_iterator&) const {} +#endif // DCHECK_IS_ON() + + const circular_deque* parent_deque_; + size_t index_; + +#if DCHECK_IS_ON() + // The generation of the parent deque when this iterator was created. The + // container will update the generation for every modification so we can + // test if the container was modified by comparing them. + uint64_t created_generation_; +#endif // DCHECK_IS_ON() +}; + +template +class circular_deque_iterator : public circular_deque_const_iterator { + using base = circular_deque_const_iterator; + + public: + friend class circular_deque; + + using difference_type = std::ptrdiff_t; + using value_type = T; + using pointer = T*; + using reference = T&; + using iterator_category = std::random_access_iterator_tag; + + // Expose the base class' constructor. + circular_deque_iterator() : circular_deque_const_iterator() {} + + // Dereferencing. + T& operator*() const { return const_cast(base::operator*()); } + T* operator->() const { return const_cast(base::operator->()); } + T& operator[](difference_type i) { + return const_cast(base::operator[](i)); + } + + // Random access mutation. + friend circular_deque_iterator operator+(const circular_deque_iterator& iter, + difference_type offset) { + circular_deque_iterator ret = iter; + ret.Add(offset); + return ret; + } + circular_deque_iterator& operator+=(difference_type offset) { + base::Add(offset); + return *this; + } + friend circular_deque_iterator operator-(const circular_deque_iterator& iter, + difference_type offset) { + circular_deque_iterator ret = iter; + ret.Add(-offset); + return ret; + } + circular_deque_iterator& operator-=(difference_type offset) { + base::Add(-offset); + return *this; + } + + // Increment and decrement. + circular_deque_iterator& operator++() { + base::Increment(); + return *this; + } + circular_deque_iterator operator++(int) { + circular_deque_iterator ret = *this; + base::Increment(); + return ret; + } + circular_deque_iterator& operator--() { + base::Decrement(); + return *this; + } + circular_deque_iterator operator--(int) { + circular_deque_iterator ret = *this; + base::Decrement(); + return ret; + } + + private: + circular_deque_iterator(const circular_deque* parent, size_t index) + : circular_deque_const_iterator(parent, index) {} +}; + +} // namespace internal + +template +class circular_deque { + private: + using VectorBuffer = internal::VectorBuffer; + + public: + using value_type = T; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + + using iterator = internal::circular_deque_iterator; + using const_iterator = internal::circular_deque_const_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + // --------------------------------------------------------------------------- + // Constructor + + constexpr circular_deque() = default; + + // Constructs with |count| copies of |value| or default constructed version. + circular_deque(size_type count) { resize(count); } + circular_deque(size_type count, const T& value) { resize(count, value); } + + // Range constructor. + template + circular_deque(InputIterator first, InputIterator last) { + assign(first, last); + } + + // Copy/move. + circular_deque(const circular_deque& other) : buffer_(other.size() + 1) { + assign(other.begin(), other.end()); + } + circular_deque(circular_deque&& other) noexcept + : buffer_(std::move(other.buffer_)), + begin_(other.begin_), + end_(other.end_) { + other.begin_ = 0; + other.end_ = 0; + } + + circular_deque(std::initializer_list init) { assign(init); } + + ~circular_deque() { DestructRange(begin_, end_); } + + // --------------------------------------------------------------------------- + // Assignments. + // + // All of these may invalidate iterators and references. + + circular_deque& operator=(const circular_deque& other) { + if (&other == this) + return *this; + + reserve(other.size()); + assign(other.begin(), other.end()); + return *this; + } + circular_deque& operator=(circular_deque&& other) noexcept { + if (&other == this) + return *this; + + // We're about to overwrite the buffer, so don't free it in clear to + // avoid doing it twice. + ClearRetainCapacity(); + buffer_ = std::move(other.buffer_); + begin_ = other.begin_; + end_ = other.end_; + + other.begin_ = 0; + other.end_ = 0; + + IncrementGeneration(); + return *this; + } + circular_deque& operator=(std::initializer_list ilist) { + reserve(ilist.size()); + assign(std::begin(ilist), std::end(ilist)); + return *this; + } + + void assign(size_type count, const value_type& value) { + ClearRetainCapacity(); + reserve(count); + for (size_t i = 0; i < count; i++) + emplace_back(value); + IncrementGeneration(); + } + + // This variant should be enabled only when InputIterator is an iterator. + template + typename std::enable_if<::base::internal::is_iterator::value, + void>::type + assign(InputIterator first, InputIterator last) { + // Possible future enhancement, dispatch on iterator tag type. For forward + // iterators we can use std::difference to preallocate the space required + // and only do one copy. + ClearRetainCapacity(); + for (; first != last; ++first) + emplace_back(*first); + IncrementGeneration(); + } + + void assign(std::initializer_list value) { + reserve(std::distance(value.begin(), value.end())); + assign(value.begin(), value.end()); + } + + // --------------------------------------------------------------------------- + // Accessors. + // + // Since this class assumes no exceptions, at() and operator[] are equivalent. + + const value_type& at(size_type i) const { + DCHECK(i < size()); + size_t right_size = buffer_.capacity() - begin_; + if (begin_ <= end_ || i < right_size) + return buffer_[begin_ + i]; + return buffer_[i - right_size]; + } + value_type& at(size_type i) { + return const_cast( + const_cast(this)->at(i)); + } + + value_type& operator[](size_type i) { return at(i); } + const value_type& operator[](size_type i) const { + return const_cast(this)->at(i); + } + + value_type& front() { + DCHECK(!empty()); + return buffer_[begin_]; + } + const value_type& front() const { + DCHECK(!empty()); + return buffer_[begin_]; + } + + value_type& back() { + DCHECK(!empty()); + return *(--end()); + } + const value_type& back() const { + DCHECK(!empty()); + return *(--end()); + } + + // --------------------------------------------------------------------------- + // Iterators. + + iterator begin() { return iterator(this, begin_); } + const_iterator begin() const { return const_iterator(this, begin_); } + const_iterator cbegin() const { return const_iterator(this, begin_); } + + iterator end() { return iterator(this, end_); } + const_iterator end() const { return const_iterator(this, end_); } + const_iterator cend() const { return const_iterator(this, end_); } + + reverse_iterator rbegin() { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const { + return const_reverse_iterator(end()); + } + const_reverse_iterator crbegin() const { return rbegin(); } + + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rend() const { + return const_reverse_iterator(begin()); + } + const_reverse_iterator crend() const { return rend(); } + + // --------------------------------------------------------------------------- + // Memory management. + + // IMPORTANT NOTE ON reserve(...): This class implements auto-shrinking of + // the buffer when elements are deleted and there is "too much" wasted space. + // So if you call reserve() with a large size in anticipation of pushing many + // elements, but pop an element before the queue is full, the capacity you + // reserved may be lost. + // + // As a result, it's only worthwhile to call reserve() when you're adding + // many things at once with no intermediate operations. + void reserve(size_type new_capacity) { + if (new_capacity > capacity()) + SetCapacityTo(new_capacity); + } + + size_type capacity() const { + // One item is wasted to indicate end(). + return buffer_.capacity() == 0 ? 0 : buffer_.capacity() - 1; + } + + void shrink_to_fit() { + if (empty()) { + // Optimize empty case to really delete everything if there was + // something. + if (buffer_.capacity()) + buffer_ = VectorBuffer(); + } else { + SetCapacityTo(size()); + } + } + + // --------------------------------------------------------------------------- + // Size management. + + // This will additionally reset the capacity() to 0. + void clear() { + // This can't resize(0) because that requires a default constructor to + // compile, which not all contained classes may implement. + ClearRetainCapacity(); + buffer_ = VectorBuffer(); + } + + bool empty() const { return begin_ == end_; } + + size_type size() const { + if (begin_ <= end_) + return end_ - begin_; + return buffer_.capacity() - begin_ + end_; + } + + // When reducing size, the elements are deleted from the end. When expanding + // size, elements are added to the end with |value| or the default + // constructed version. Even when using resize(count) to shrink, a default + // constructor is required for the code to compile, even though it will not + // be called. + // + // There are two versions rather than using a default value to avoid + // creating a temporary when shrinking (when it's not needed). Plus if + // the default constructor is desired when expanding usually just calling it + // for each element is faster than making a default-constructed temporary and + // copying it. + void resize(size_type count) { + // SEE BELOW VERSION if you change this. The code is mostly the same. + if (count > size()) { + // This could be slighly more efficient but expanding a queue with + // identical elements is unusual and the extra computations of emplacing + // one-by-one will typically be small relative to calling the constructor + // for every item. + ExpandCapacityIfNecessary(count - size()); + while (size() < count) + emplace_back(); + } else if (count < size()) { + size_t new_end = (begin_ + count) % buffer_.capacity(); + DestructRange(new_end, end_); + end_ = new_end; + + ShrinkCapacityIfNecessary(); + } + IncrementGeneration(); + } + void resize(size_type count, const value_type& value) { + // SEE ABOVE VERSION if you change this. The code is mostly the same. + if (count > size()) { + ExpandCapacityIfNecessary(count - size()); + while (size() < count) + emplace_back(value); + } else if (count < size()) { + size_t new_end = (begin_ + count) % buffer_.capacity(); + DestructRange(new_end, end_); + end_ = new_end; + + ShrinkCapacityIfNecessary(); + } + IncrementGeneration(); + } + + // --------------------------------------------------------------------------- + // Insert and erase. + // + // Insertion and deletion in the middle is O(n) and invalidates all existing + // iterators. + // + // The implementation of insert isn't optimized as much as it could be. If + // the insertion requires that the buffer be grown, it will first be grown + // and everything moved, and then the items will be inserted, potentially + // moving some items twice. This simplifies the implemntation substantially + // and means less generated templatized code. Since this is an uncommon + // operation for deques, and already relatively slow, it doesn't seem worth + // the benefit to optimize this. + + void insert(const_iterator pos, size_type count, const T& value) { + ValidateIterator(pos); + + // Optimize insert at the beginning. + if (pos == begin()) { + ExpandCapacityIfNecessary(count); + for (size_t i = 0; i < count; i++) + push_front(value); + return; + } + + iterator insert_cur(this, pos.index_); + iterator insert_end; + MakeRoomFor(count, &insert_cur, &insert_end); + while (insert_cur < insert_end) { + new (&buffer_[insert_cur.index_]) T(value); + ++insert_cur; + } + + IncrementGeneration(); + } + + // This enable_if keeps this call from getting confused with the (pos, count, + // value) version when value is an integer. + template + typename std::enable_if<::base::internal::is_iterator::value, + void>::type + insert(const_iterator pos, InputIterator first, InputIterator last) { + ValidateIterator(pos); + + size_t inserted_items = std::distance(first, last); + if (inserted_items == 0) + return; // Can divide by 0 when doing modulo below, so return early. + + // Make a hole to copy the items into. + iterator insert_cur; + iterator insert_end; + if (pos == begin()) { + // Optimize insert at the beginning, nothing needs to be shifted and the + // hole is the |inserted_items| block immediately before |begin_|. + ExpandCapacityIfNecessary(inserted_items); + insert_end = begin(); + begin_ = + (begin_ + buffer_.capacity() - inserted_items) % buffer_.capacity(); + insert_cur = begin(); + } else { + insert_cur = iterator(this, pos.index_); + MakeRoomFor(inserted_items, &insert_cur, &insert_end); + } + + // Copy the items. + while (insert_cur < insert_end) { + new (&buffer_[insert_cur.index_]) T(*first); + ++insert_cur; + ++first; + } + + IncrementGeneration(); + } + + // These all return an iterator to the inserted item. Existing iterators will + // be invalidated. + iterator insert(const_iterator pos, const T& value) { + return emplace(pos, value); + } + iterator insert(const_iterator pos, T&& value) { + return emplace(pos, std::move(value)); + } + template + iterator emplace(const_iterator pos, Args&&... args) { + ValidateIterator(pos); + + // Optimize insert at beginning which doesn't require shifting. + if (pos == cbegin()) { + emplace_front(std::forward(args)...); + return begin(); + } + + // Do this before we make the new iterators we return. + IncrementGeneration(); + + iterator insert_begin(this, pos.index_); + iterator insert_end; + MakeRoomFor(1, &insert_begin, &insert_end); + new (&buffer_[insert_begin.index_]) T(std::forward(args)...); + + return insert_begin; + } + + // Calling erase() won't automatically resize the buffer smaller like resize + // or the pop functions. Erase is slow and relatively uncommon, and for + // normal deque usage a pop will normally be done on a regular basis that + // will prevent excessive buffer usage over long periods of time. It's not + // worth having the extra code for every template instantiation of erase() + // to resize capacity downward to a new buffer. + iterator erase(const_iterator pos) { return erase(pos, pos + 1); } + iterator erase(const_iterator first, const_iterator last) { + ValidateIterator(first); + ValidateIterator(last); + + IncrementGeneration(); + + // First, call the destructor on the deleted items. + if (first.index_ == last.index_) { + // Nothing deleted. Need to return early to avoid falling through to + // moving items on top of themselves. + return iterator(this, first.index_); + } else if (first.index_ < last.index_) { + // Contiguous range. + buffer_.DestructRange(&buffer_[first.index_], &buffer_[last.index_]); + } else { + // Deleted range wraps around. + buffer_.DestructRange(&buffer_[first.index_], + &buffer_[buffer_.capacity()]); + buffer_.DestructRange(&buffer_[0], &buffer_[last.index_]); + } + + if (first.index_ == begin_) { + // This deletion is from the beginning. Nothing needs to be copied, only + // begin_ needs to be updated. + begin_ = last.index_; + return iterator(this, last.index_); + } + + // In an erase operation, the shifted items all move logically to the left, + // so move them from left-to-right. + iterator move_src(this, last.index_); + iterator move_src_end = end(); + iterator move_dest(this, first.index_); + for (; move_src < move_src_end; move_src++, move_dest++) { + buffer_.MoveRange(&buffer_[move_src.index_], + &buffer_[move_src.index_ + 1], + &buffer_[move_dest.index_]); + } + + end_ = move_dest.index_; + + // Since we did not reallocate and only changed things after the erase + // element(s), the input iterator's index points to the thing following the + // deletion. + return iterator(this, first.index_); + } + + // --------------------------------------------------------------------------- + // Begin/end operations. + + void push_front(const T& value) { emplace_front(value); } + void push_front(T&& value) { emplace_front(std::move(value)); } + + void push_back(const T& value) { emplace_back(value); } + void push_back(T&& value) { emplace_back(std::move(value)); } + + template + reference emplace_front(Args&&... args) { + ExpandCapacityIfNecessary(1); + if (begin_ == 0) + begin_ = buffer_.capacity() - 1; + else + begin_--; + IncrementGeneration(); + new (&buffer_[begin_]) T(std::forward(args)...); + return front(); + } + + template + reference emplace_back(Args&&... args) { + ExpandCapacityIfNecessary(1); + new (&buffer_[end_]) T(std::forward(args)...); + if (end_ == buffer_.capacity() - 1) + end_ = 0; + else + end_++; + IncrementGeneration(); + return back(); + } + + void pop_front() { + DCHECK(size()); + buffer_.DestructRange(&buffer_[begin_], &buffer_[begin_ + 1]); + begin_++; + if (begin_ == buffer_.capacity()) + begin_ = 0; + + ShrinkCapacityIfNecessary(); + + // Technically popping will not invalidate any iterators since the + // underlying buffer will be stable. But in the future we may want to add a + // feature that resizes the buffer smaller if there is too much wasted + // space. This ensures we can make such a change safely. + IncrementGeneration(); + } + void pop_back() { + DCHECK(size()); + if (end_ == 0) + end_ = buffer_.capacity() - 1; + else + end_--; + buffer_.DestructRange(&buffer_[end_], &buffer_[end_ + 1]); + + ShrinkCapacityIfNecessary(); + + // See pop_front comment about why this is here. + IncrementGeneration(); + } + + // --------------------------------------------------------------------------- + // General operations. + + void swap(circular_deque& other) { + std::swap(buffer_, other.buffer_); + std::swap(begin_, other.begin_); + std::swap(end_, other.end_); + IncrementGeneration(); + } + + friend void swap(circular_deque& lhs, circular_deque& rhs) { lhs.swap(rhs); } + + private: + friend internal::circular_deque_iterator; + friend internal::circular_deque_const_iterator; + + // Moves the items in the given circular buffer to the current one. The + // source is moved from so will become invalid. The destination buffer must + // have already been allocated with enough size. + static void MoveBuffer(VectorBuffer& from_buf, + size_t from_begin, + size_t from_end, + VectorBuffer* to_buf, + size_t* to_begin, + size_t* to_end) { + size_t from_capacity = from_buf.capacity(); + + *to_begin = 0; + if (from_begin < from_end) { + // Contiguous. + from_buf.MoveRange(&from_buf[from_begin], &from_buf[from_end], + to_buf->begin()); + *to_end = from_end - from_begin; + } else if (from_begin > from_end) { + // Discontiguous, copy the right side to the beginning of the new buffer. + from_buf.MoveRange(&from_buf[from_begin], &from_buf[from_capacity], + to_buf->begin()); + size_t right_size = from_capacity - from_begin; + // Append the left side. + from_buf.MoveRange(&from_buf[0], &from_buf[from_end], + &(*to_buf)[right_size]); + *to_end = right_size + from_end; + } else { + // No items. + *to_end = 0; + } + } + + // Expands the buffer size. This assumes the size is larger than the + // number of elements in the vector (it won't call delete on anything). + void SetCapacityTo(size_t new_capacity) { + // Use the capacity + 1 as the internal buffer size to differentiate + // empty and full (see definition of buffer_ below). + VectorBuffer new_buffer(new_capacity + 1); + MoveBuffer(buffer_, begin_, end_, &new_buffer, &begin_, &end_); + buffer_ = std::move(new_buffer); + } + void ExpandCapacityIfNecessary(size_t additional_elts) { + size_t min_new_capacity = size() + additional_elts; + if (capacity() >= min_new_capacity) + return; // Already enough room. + + min_new_capacity = + std::max(min_new_capacity, internal::kCircularBufferInitialCapacity); + + // std::vector always grows by at least 50%. WTF::Deque grows by at least + // 25%. We expect queue workloads to generally stay at a similar size and + // grow less than a vector might, so use 25%. + size_t new_capacity = + std::max(min_new_capacity, capacity() + capacity() / 4); + SetCapacityTo(new_capacity); + } + + void ShrinkCapacityIfNecessary() { + // Don't auto-shrink below this size. + if (capacity() <= internal::kCircularBufferInitialCapacity) + return; + + // Shrink when 100% of the size() is wasted. + size_t sz = size(); + size_t empty_spaces = capacity() - sz; + if (empty_spaces < sz) + return; + + // Leave 1/4 the size as free capacity, not going below the initial + // capacity. + size_t new_capacity = + std::max(internal::kCircularBufferInitialCapacity, sz + sz / 4); + if (new_capacity < capacity()) { + // Count extra item to convert to internal capacity. + SetCapacityTo(new_capacity); + } + } + + // Backend for clear() but does not resize the internal buffer. + void ClearRetainCapacity() { + // This can't resize(0) because that requires a default constructor to + // compile, which not all contained classes may implement. + DestructRange(begin_, end_); + begin_ = 0; + end_ = 0; + IncrementGeneration(); + } + + // Calls destructors for the given begin->end indices. The indices may wrap + // around. The buffer is not resized, and the begin_ and end_ members are + // not changed. + void DestructRange(size_t begin, size_t end) { + if (end == begin) { + return; + } else if (end > begin) { + buffer_.DestructRange(&buffer_[begin], &buffer_[end]); + } else { + buffer_.DestructRange(&buffer_[begin], &buffer_[buffer_.capacity()]); + buffer_.DestructRange(&buffer_[0], &buffer_[end]); + } + } + + // Makes room for |count| items starting at |*insert_begin|. Since iterators + // are not stable across buffer resizes, |*insert_begin| will be updated to + // point to the beginning of the newly opened position in the new array (it's + // in/out), and the end of the newly opened position (it's out-only). + void MakeRoomFor(size_t count, iterator* insert_begin, iterator* insert_end) { + if (count == 0) { + *insert_end = *insert_begin; + return; + } + + // The offset from the beginning will be stable across reallocations. + size_t begin_offset = insert_begin->OffsetFromBegin(); + ExpandCapacityIfNecessary(count); + + insert_begin->index_ = (begin_ + begin_offset) % buffer_.capacity(); + *insert_end = + iterator(this, (insert_begin->index_ + count) % buffer_.capacity()); + + // Update the new end and prepare the iterators for copying. + iterator src = end(); + end_ = (end_ + count) % buffer_.capacity(); + iterator dest = end(); + + // Move the elements. This will always involve shifting logically to the + // right, so move in a right-to-left order. + while (true) { + if (src == *insert_begin) + break; + --src; + --dest; + buffer_.MoveRange(&buffer_[src.index_], &buffer_[src.index_ + 1], + &buffer_[dest.index_]); + } + } + +#if DCHECK_IS_ON() + // Asserts the given index is dereferencable. The index is an index into the + // buffer, not an index used by operator[] or at() which will be offsets from + // begin. + void CheckValidIndex(size_t i) const { + if (begin_ <= end_) + DCHECK(i >= begin_ && i < end_); + else + DCHECK((i >= begin_ && i < buffer_.capacity()) || i < end_); + } + + // Asserts the given index is either dereferencable or points to end(). + void CheckValidIndexOrEnd(size_t i) const { + if (i != end_) + CheckValidIndex(i); + } + + void ValidateIterator(const const_iterator& i) const { + DCHECK(i.parent_deque_ == this); + i.CheckUnstableUsage(); + } + + // See generation_ below. + void IncrementGeneration() { generation_++; } +#else + // No-op versions of these functions for release builds. + void CheckValidIndex(size_t) const {} + void CheckValidIndexOrEnd(size_t) const {} + void ValidateIterator(const const_iterator& i) const {} + void IncrementGeneration() {} +#endif + + // Danger, the buffer_.capacity() is the "internal capacity" which is + // capacity() + 1 since there is an extra item to indicate the end. Otherwise + // being completely empty and completely full are indistinguishable (begin == + // end). We could add a separate flag to avoid it, but that adds significant + // extra complexity since every computation will have to check for it. Always + // keeping one extra unused element in the buffer makes iterator computations + // much simpler. + // + // Container internal code will want to use buffer_.capacity() for offset + // computations rather than capacity(). + VectorBuffer buffer_; + size_type begin_ = 0; + size_type end_ = 0; + +#if DCHECK_IS_ON() + // Incremented every time a modification is made that could affect iterator + // invalidations. + uint64_t generation_ = 0; +#endif +}; + +// Implementations of base::Erase[If] (see base/stl_util.h). +template +void Erase(circular_deque& container, const Value& value) { + container.erase(std::remove(container.begin(), container.end(), value), + container.end()); +} + +template +void EraseIf(circular_deque& container, Predicate pred) { + container.erase(std::remove_if(container.begin(), container.end(), pred), + container.end()); +} + +} // namespace base + +#endif // BASE_CONTAINERS_CIRCULAR_DEQUE_H_ diff --git a/native/zucchini/vendor/libchrome/base/containers/queue.h b/native/zucchini/vendor/libchrome/base/containers/queue.h new file mode 100644 index 0000000..b5bc5c3 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/containers/queue.h @@ -0,0 +1,23 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_CONTAINERS_QUEUE_H_ +#define BASE_CONTAINERS_QUEUE_H_ + +#include + +#include "base/containers/circular_deque.h" + +namespace base { + +// Provides a definition of base::queue that's like std::queue but uses a +// base::circular_deque instead of std::deque. Since std::queue is just a +// wrapper for an underlying type, we can just provide a typedef for it that +// defaults to the base circular_deque. +template > +using queue = std::queue; + +} // namespace base + +#endif // BASE_CONTAINERS_QUEUE_H_ diff --git a/native/zucchini/vendor/libchrome/base/containers/span.h b/native/zucchini/vendor/libchrome/base/containers/span.h new file mode 100644 index 0000000..f1e0000 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/containers/span.h @@ -0,0 +1,453 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_CONTAINERS_SPAN_H_ +#define BASE_CONTAINERS_SPAN_H_ + +#include + +#include +#include +#include +#include +#include + +#include "base/logging.h" +#include "base/stl_util.h" + +namespace base { + +// [views.constants] +constexpr size_t dynamic_extent = static_cast(-1); + +template +class span; + +namespace internal { + +template +struct IsSpanImpl : std::false_type {}; + +template +struct IsSpanImpl> : std::true_type {}; + +template +using IsSpan = IsSpanImpl>; + +template +struct IsStdArrayImpl : std::false_type {}; + +template +struct IsStdArrayImpl> : std::true_type {}; + +template +using IsStdArray = IsStdArrayImpl>; + +template +using IsCArray = std::is_array>; + +template +using IsLegalDataConversion = std::is_convertible; + +template +using ContainerHasConvertibleData = IsLegalDataConversion< + std::remove_pointer_t()))>, + T>; + +template +using ContainerHasIntegralSize = + std::is_integral()))>; + +template +using EnableIfLegalSpanConversion = + std::enable_if_t<(ToExtent == dynamic_extent || ToExtent == FromExtent) && + IsLegalDataConversion::value>; + +// SFINAE check if Array can be converted to a span. +template +using EnableIfSpanCompatibleArray = + std::enable_if_t<(Extent == dynamic_extent || Extent == N) && + ContainerHasConvertibleData::value>; + +// SFINAE check if Container can be converted to a span. +template +using EnableIfSpanCompatibleContainer = + std::enable_if_t::value && + !internal::IsStdArray::value && + !internal::IsCArray::value && + ContainerHasConvertibleData::value && + ContainerHasIntegralSize::value>; + +} // namespace internal + +// A span is a value type that represents an array of elements of type T. Since +// it only consists of a pointer to memory with an associated size, it is very +// light-weight. It is cheap to construct, copy, move and use spans, so that +// users are encouraged to use it as a pass-by-value parameter. A span does not +// own the underlying memory, so care must be taken to ensure that a span does +// not outlive the backing store. +// +// span is somewhat analogous to StringPiece, but with arbitrary element types, +// allowing mutation if T is non-const. +// +// span is implicitly convertible from C++ arrays, as well as most [1] +// container-like types that provide a data() and size() method (such as +// std::vector). A mutable span can also be implicitly converted to an +// immutable span. +// +// Consider using a span for functions that take a data pointer and size +// parameter: it allows the function to still act on an array-like type, while +// allowing the caller code to be a bit more concise. +// +// For read-only data access pass a span: the caller can supply either +// a span or a span, while the callee will have a read-only view. +// For read-write access a mutable span is required. +// +// Without span: +// Read-Only: +// // std::string HexEncode(const uint8_t* data, size_t size); +// std::vector data_buffer = GenerateData(); +// std::string r = HexEncode(data_buffer.data(), data_buffer.size()); +// +// Mutable: +// // ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt, Args...); +// char str_buffer[100]; +// SafeSNPrintf(str_buffer, sizeof(str_buffer), "Pi ~= %lf", 3.14); +// +// With span: +// Read-Only: +// // std::string HexEncode(base::span data); +// std::vector data_buffer = GenerateData(); +// std::string r = HexEncode(data_buffer); +// +// Mutable: +// // ssize_t SafeSNPrintf(base::span, const char* fmt, Args...); +// char str_buffer[100]; +// SafeSNPrintf(str_buffer, "Pi ~= %lf", 3.14); +// +// Spans with "const" and pointers +// ------------------------------- +// +// Const and pointers can get confusing. Here are vectors of pointers and their +// corresponding spans: +// +// const std::vector => base::span +// std::vector => base::span +// const std::vector => base::span +// +// Differences from the working group proposal +// ------------------------------------------- +// +// https://wg21.link/P0122 is the latest working group proposal, Chromium +// currently implements R7. Differences between the proposal and the +// implementation are documented in subsections below. +// +// Differences from [span.objectrep]: +// - as_bytes() and as_writable_bytes() return spans of uint8_t instead of +// std::byte +// +// Differences in constants and types: +// - index_type is aliased to size_t +// +// Differences from [span.sub]: +// - using size_t instead of ptrdiff_t for indexing +// +// Differences from [span.obs]: +// - using size_t instead of ptrdiff_t to represent size() +// +// Differences from [span.elem]: +// - using size_t instead of ptrdiff_t for indexing +// +// Furthermore, all constructors and methods are marked noexcept due to the lack +// of exceptions in Chromium. +// +// Due to the lack of class template argument deduction guides in C++14 +// appropriate make_span() utility functions are provided. + +// [span], class template span +template +class span { + public: + using element_type = T; + using value_type = std::remove_cv_t; + using index_type = size_t; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; + using iterator = T*; + using const_iterator = const T*; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + static constexpr index_type extent = Extent; + + // [span.cons], span constructors, copy, assignment, and destructor + constexpr span() noexcept : data_(nullptr), size_(0) { + static_assert(Extent == dynamic_extent || Extent == 0, "Invalid Extent"); + } + + constexpr span(T* data, size_t size) noexcept : data_(data), size_(size) { + CHECK(Extent == dynamic_extent || Extent == size); + } + + // Artificially templatized to break ambiguity for span(ptr, 0). + template + constexpr span(T* begin, T* end) noexcept : span(begin, end - begin) { + // Note: CHECK_LE is not constexpr, hence regular CHECK must be used. + CHECK(begin <= end); + } + + template < + size_t N, + typename = internal::EnableIfSpanCompatibleArray> + constexpr span(T (&array)[N]) noexcept : span(base::data(array), N) {} + + template < + size_t N, + typename = internal:: + EnableIfSpanCompatibleArray&, N, T, Extent>> + constexpr span(std::array& array) noexcept + : span(base::data(array), N) {} + + template &, + N, + T, + Extent>> + constexpr span(const std::array& array) noexcept + : span(base::data(array), N) {} + + // Conversion from a container that has compatible base::data() and integral + // base::size(). + template > + constexpr span(Container& container) noexcept + : span(base::data(container), base::size(container)) {} + + template < + typename Container, + typename = internal::EnableIfSpanCompatibleContainer> + span(const Container& container) noexcept + : span(base::data(container), base::size(container)) {} + + constexpr span(const span& other) noexcept = default; + + // Conversions from spans of compatible types and extents: this allows a + // span to be seamlessly used as a span, but not the other way + // around. If extent is not dynamic, OtherExtent has to be equal to Extent. + template < + typename U, + size_t OtherExtent, + typename = + internal::EnableIfLegalSpanConversion> + constexpr span(const span& other) + : span(other.data(), other.size()) {} + + constexpr span& operator=(const span& other) noexcept = default; + ~span() noexcept = default; + + // [span.sub], span subviews + template + constexpr span first() const noexcept { + static_assert(Extent == dynamic_extent || Count <= Extent, + "Count must not exceed Extent"); + CHECK(Extent != dynamic_extent || Count <= size()); + return {data(), Count}; + } + + template + constexpr span last() const noexcept { + static_assert(Extent == dynamic_extent || Count <= Extent, + "Count must not exceed Extent"); + CHECK(Extent != dynamic_extent || Count <= size()); + return {data() + (size() - Count), Count}; + } + + template + constexpr span + subspan() const noexcept { + static_assert(Extent == dynamic_extent || Offset <= Extent, + "Offset must not exceed Extent"); + static_assert(Extent == dynamic_extent || Count == dynamic_extent || + Count <= Extent - Offset, + "Count must not exceed Extent - Offset"); + CHECK(Extent != dynamic_extent || Offset <= size()); + CHECK(Extent != dynamic_extent || Count == dynamic_extent || + Count <= size() - Offset); + return {data() + Offset, Count != dynamic_extent ? Count : size() - Offset}; + } + + constexpr span first(size_t count) const noexcept { + // Note: CHECK_LE is not constexpr, hence regular CHECK must be used. + CHECK(count <= size()); + return {data(), count}; + } + + constexpr span last(size_t count) const noexcept { + // Note: CHECK_LE is not constexpr, hence regular CHECK must be used. + CHECK(count <= size()); + return {data() + (size() - count), count}; + } + + constexpr span subspan(size_t offset, + size_t count = dynamic_extent) const + noexcept { + // Note: CHECK_LE is not constexpr, hence regular CHECK must be used. + CHECK(offset <= size()); + CHECK(count == dynamic_extent || count <= size() - offset); + return {data() + offset, count != dynamic_extent ? count : size() - offset}; + } + + // [span.obs], span observers + constexpr size_t size() const noexcept { return size_; } + constexpr size_t size_bytes() const noexcept { return size() * sizeof(T); } + constexpr bool empty() const noexcept { return size() == 0; } + + // [span.elem], span element access + constexpr T& operator[](size_t idx) const noexcept { + // Note: CHECK_LT is not constexpr, hence regular CHECK must be used. + CHECK(idx < size()); + return *(data() + idx); + } + + constexpr T& operator()(size_t idx) const noexcept { + // Note: CHECK_LT is not constexpr, hence regular CHECK must be used. + CHECK(idx < size()); + return *(data() + idx); + } + + constexpr T* data() const noexcept { return data_; } + + // [span.iter], span iterator support + constexpr iterator begin() const noexcept { return data(); } + constexpr iterator end() const noexcept { return data() + size(); } + + constexpr const_iterator cbegin() const noexcept { return begin(); } + constexpr const_iterator cend() const noexcept { return end(); } + + constexpr reverse_iterator rbegin() const noexcept { + return reverse_iterator(end()); + } + constexpr reverse_iterator rend() const noexcept { + return reverse_iterator(begin()); + } + + constexpr const_reverse_iterator crbegin() const noexcept { + return const_reverse_iterator(cend()); + } + constexpr const_reverse_iterator crend() const noexcept { + return const_reverse_iterator(cbegin()); + } + + private: + T* data_; + size_t size_; +}; + +// span::extent can not be declared inline prior to C++17, hence this +// definition is required. +template +constexpr size_t span::extent; + +// [span.comparison], span comparison operators +// Relational operators. Equality is a element-wise comparison. +template +constexpr bool operator==(span lhs, span rhs) noexcept { + return std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend()); +} + +template +constexpr bool operator!=(span lhs, span rhs) noexcept { + return !(lhs == rhs); +} + +template +constexpr bool operator<(span lhs, span rhs) noexcept { + return std::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(), + rhs.cend()); +} + +template +constexpr bool operator<=(span lhs, span rhs) noexcept { + return !(rhs < lhs); +} + +template +constexpr bool operator>(span lhs, span rhs) noexcept { + return rhs < lhs; +} + +template +constexpr bool operator>=(span lhs, span rhs) noexcept { + return !(lhs < rhs); +} + +// [span.objectrep], views of object representation +template +span +as_bytes(span s) noexcept { + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +template ::value>> +span +as_writable_bytes(span s) noexcept { + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +// Type-deducing helpers for constructing a span. +template +constexpr span make_span(T* data, size_t size) noexcept { + return {data, size}; +} + +template +constexpr span make_span(T* begin, T* end) noexcept { + return {begin, end}; +} + +template +constexpr span make_span(T (&array)[N]) noexcept { + return array; +} + +template +constexpr span make_span(std::array& array) noexcept { + return array; +} + +template +constexpr span make_span(const std::array& array) noexcept { + return array; +} + +template > +constexpr span make_span(Container& container) noexcept { + return container; +} + +template < + typename Container, + typename T = const typename Container::value_type, + typename = internal::EnableIfSpanCompatibleContainer> +constexpr span make_span(const Container& container) noexcept { + return container; +} + +template +constexpr span make_span(const span& span) noexcept { + return span; +} + +} // namespace base + +#endif // BASE_CONTAINERS_SPAN_H_ diff --git a/native/zucchini/vendor/libchrome/base/containers/stack.h b/native/zucchini/vendor/libchrome/base/containers/stack.h new file mode 100644 index 0000000..5cf06f8 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/containers/stack.h @@ -0,0 +1,23 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_CONTAINERS_STACK_H_ +#define BASE_CONTAINERS_STACK_H_ + +#include + +#include "base/containers/circular_deque.h" + +namespace base { + +// Provides a definition of base::stack that's like std::stack but uses a +// base::circular_deque instead of std::deque. Since std::stack is just a +// wrapper for an underlying type, we can just provide a typedef for it that +// defaults to the base circular_deque. +template > +using stack = std::stack; + +} // namespace base + +#endif // BASE_CONTAINERS_STACK_H_ diff --git a/native/zucchini/vendor/libchrome/base/containers/vector_buffer.h b/native/zucchini/vendor/libchrome/base/containers/vector_buffer.h new file mode 100644 index 0000000..a72c1ed --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/containers/vector_buffer.h @@ -0,0 +1,163 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_CONTAINERS_VECTOR_BUFFERS_H_ +#define BASE_CONTAINERS_VECTOR_BUFFERS_H_ + +#include +#include + +#include +#include + +#include "base/logging.h" +#include "base/macros.h" + +namespace base { +namespace internal { + +// Internal implementation detail of base/containers. +// +// Implements a vector-like buffer that holds a certain capacity of T. Unlike +// std::vector, VectorBuffer never constructs or destructs its arguments, and +// can't change sizes. But it does implement templates to assist in efficient +// moving and destruction of those items manually. +// +// In particular, the destructor function does not iterate over the items if +// there is no destructor. Moves should be implemented as a memcpy/memmove for +// trivially copyable objects (POD) otherwise, it should be a std::move if +// possible, and as a last resort it falls back to a copy. This behavior is +// similar to std::vector. +// +// No special consideration is done for noexcept move constructors since +// we compile without exceptions. +// +// The current API does not support moving overlapping ranges. +template +class VectorBuffer { + public: + constexpr VectorBuffer() = default; + +#if defined(__clang__) && !defined(__native_client__) + // This constructor converts an uninitialized void* to a T* which triggers + // clang Control Flow Integrity. Since this is as-designed, disable. + __attribute__((no_sanitize("cfi-unrelated-cast", "vptr"))) +#endif + VectorBuffer(size_t count) + : buffer_(reinterpret_cast(malloc(sizeof(T) * count))), + capacity_(count) { + } + VectorBuffer(VectorBuffer&& other) noexcept + : buffer_(other.buffer_), capacity_(other.capacity_) { + other.buffer_ = nullptr; + other.capacity_ = 0; + } + + ~VectorBuffer() { free(buffer_); } + + VectorBuffer& operator=(VectorBuffer&& other) { + free(buffer_); + buffer_ = other.buffer_; + capacity_ = other.capacity_; + + other.buffer_ = nullptr; + other.capacity_ = 0; + return *this; + } + + size_t capacity() const { return capacity_; } + + T& operator[](size_t i) { return buffer_[i]; } + const T& operator[](size_t i) const { return buffer_[i]; } + T* begin() { return buffer_; } + T* end() { return &buffer_[capacity_]; } + + // DestructRange ------------------------------------------------------------ + + // Trivially destructible objects need not have their destructors called. + template ::value, + int>::type = 0> + void DestructRange(T* begin, T* end) {} + + // Non-trivially destructible objects must have their destructors called + // individually. + template ::value, + int>::type = 0> + void DestructRange(T* begin, T* end) { + while (begin != end) { + begin->~T(); + begin++; + } + } + + // MoveRange ---------------------------------------------------------------- + // + // The destructor will be called (as necessary) for all moved types. The + // ranges must not overlap. + // + // The parameters and begin and end (one past the last) of the input buffer, + // and the address of the first element to copy to. There must be sufficient + // room in the destination for all items in the range [begin, end). + + // Trivially copyable types can use memcpy. trivially copyable implies + // that there is a trivial destructor as we don't have to call it. + template ::value, + int>::type = 0> + static void MoveRange(T* from_begin, T* from_end, T* to) { + DCHECK(!RangesOverlap(from_begin, from_end, to)); + memcpy(to, from_begin, (from_end - from_begin) * sizeof(T)); + } + + // Not trivially copyable, but movable: call the move constructor and + // destruct the original. + template ::value && + !base::is_trivially_copyable::value, + int>::type = 0> + static void MoveRange(T* from_begin, T* from_end, T* to) { + DCHECK(!RangesOverlap(from_begin, from_end, to)); + while (from_begin != from_end) { + new (to) T(std::move(*from_begin)); + from_begin->~T(); + from_begin++; + to++; + } + } + + // Not movable, not trivially copyable: call the copy constructor and + // destruct the original. + template ::value && + !base::is_trivially_copyable::value, + int>::type = 0> + static void MoveRange(T* from_begin, T* from_end, T* to) { + DCHECK(!RangesOverlap(from_begin, from_end, to)); + while (from_begin != from_end) { + new (to) T(*from_begin); + from_begin->~T(); + from_begin++; + to++; + } + } + + private: + static bool RangesOverlap(const T* from_begin, + const T* from_end, + const T* to) { + return !(to >= from_end || to + (from_end - from_begin) <= from_begin); + } + + T* buffer_ = nullptr; + size_t capacity_ = 0; + + DISALLOW_COPY_AND_ASSIGN(VectorBuffer); +}; + +} // namespace internal +} // namespace base + +#endif // BASE_CONTAINERS_VECTOR_BUFFERS_H_ diff --git a/native/zucchini/vendor/libchrome/base/debug/activity_tracker.cc b/native/zucchini/vendor/libchrome/base/debug/activity_tracker.cc new file mode 100644 index 0000000..24cfa5a --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/debug/activity_tracker.cc @@ -0,0 +1,1828 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/debug/activity_tracker.h" + +#include +#include +#include + +#include "base/atomic_sequence_num.h" +#include "base/debug/stack_trace.h" +#include "base/files/file.h" +#include "base/files/file_path.h" +#include "base/files/memory_mapped_file.h" +#include "base/logging.h" +#include "base/memory/ptr_util.h" +#include "base/metrics/field_trial.h" +#include "base/metrics/histogram_macros.h" +#include "base/pending_task.h" +#include "base/pickle.h" +#include "base/process/process.h" +#include "base/process/process_handle.h" +#include "base/stl_util.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "base/threading/platform_thread.h" +#include "build/build_config.h" + +namespace base { +namespace debug { + +namespace { + +// The minimum depth a stack should support. +const int kMinStackDepth = 2; + +// The amount of memory set aside for holding arbitrary user data (key/value +// pairs) globally or associated with ActivityData entries. +const size_t kUserDataSize = 1 << 10; // 1 KiB +const size_t kProcessDataSize = 4 << 10; // 4 KiB +const size_t kMaxUserDataNameLength = + static_cast(std::numeric_limits::max()); + +// A constant used to indicate that module information is changing. +const uint32_t kModuleInformationChanging = 0x80000000; + +// The key used to record process information. +const char kProcessPhaseDataKey[] = "process-phase"; + +// An atomically incrementing number, used to check for recreations of objects +// in the same memory space. +AtomicSequenceNumber g_next_id; + +// Gets the next non-zero identifier. It is only unique within a process. +uint32_t GetNextDataId() { + uint32_t id; + while ((id = g_next_id.GetNext()) == 0) + ; + return id; +} + +// Gets the current process-id, either from the GlobalActivityTracker if it +// exists (where the PID can be defined for testing) or from the system if +// there isn't such. +int64_t GetProcessId() { + GlobalActivityTracker* global = GlobalActivityTracker::Get(); + if (global) + return global->process_id(); + return GetCurrentProcId(); +} + +// Finds and reuses a specific allocation or creates a new one. +PersistentMemoryAllocator::Reference AllocateFrom( + PersistentMemoryAllocator* allocator, + uint32_t from_type, + size_t size, + uint32_t to_type) { + PersistentMemoryAllocator::Iterator iter(allocator); + PersistentMemoryAllocator::Reference ref; + while ((ref = iter.GetNextOfType(from_type)) != 0) { + DCHECK_LE(size, allocator->GetAllocSize(ref)); + // This can fail if a another thread has just taken it. It is assumed that + // the memory is cleared during the "free" operation. + if (allocator->ChangeType(ref, to_type, from_type, /*clear=*/false)) + return ref; + } + + return allocator->Allocate(size, to_type); +} + +// Determines the previous aligned index. +size_t RoundDownToAlignment(size_t index, size_t alignment) { + return index & (0 - alignment); +} + +// Determines the next aligned index. +size_t RoundUpToAlignment(size_t index, size_t alignment) { + return (index + (alignment - 1)) & (0 - alignment); +} + +// Converts "tick" timing into wall time. +Time WallTimeFromTickTime(int64_t ticks_start, int64_t ticks, Time time_start) { + return time_start + TimeDelta::FromInternalValue(ticks - ticks_start); +} + +} // namespace + +union ThreadRef { + int64_t as_id; +#if defined(OS_WIN) + // On Windows, the handle itself is often a pseudo-handle with a common + // value meaning "this thread" and so the thread-id is used. The former + // can be converted to a thread-id with a system call. + PlatformThreadId as_tid; +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + // On Posix and Fuchsia, the handle is always a unique identifier so no + // conversion needs to be done. However, its value is officially opaque so + // there is no one correct way to convert it to a numerical identifier. + PlatformThreadHandle::Handle as_handle; +#endif +}; + +OwningProcess::OwningProcess() = default; +OwningProcess::~OwningProcess() = default; + +void OwningProcess::Release_Initialize(int64_t pid) { + uint32_t old_id = data_id.load(std::memory_order_acquire); + DCHECK_EQ(0U, old_id); + process_id = pid != 0 ? pid : GetProcessId(); + create_stamp = Time::Now().ToInternalValue(); + data_id.store(GetNextDataId(), std::memory_order_release); +} + +void OwningProcess::SetOwningProcessIdForTesting(int64_t pid, int64_t stamp) { + DCHECK_NE(0U, data_id); + process_id = pid; + create_stamp = stamp; +} + +// static +bool OwningProcess::GetOwningProcessId(const void* memory, + int64_t* out_id, + int64_t* out_stamp) { + const OwningProcess* info = reinterpret_cast(memory); + uint32_t id = info->data_id.load(std::memory_order_acquire); + if (id == 0) + return false; + + *out_id = info->process_id; + *out_stamp = info->create_stamp; + return id == info->data_id.load(std::memory_order_seq_cst); +} + +// It doesn't matter what is contained in this (though it will be all zeros) +// as only the address of it is important. +const ActivityData kNullActivityData = {}; + +ActivityData ActivityData::ForThread(const PlatformThreadHandle& handle) { + ThreadRef thread_ref; + thread_ref.as_id = 0; // Zero the union in case other is smaller. +#if defined(OS_WIN) + thread_ref.as_tid = ::GetThreadId(handle.platform_handle()); +#elif defined(OS_POSIX) + thread_ref.as_handle = handle.platform_handle(); +#endif + return ForThread(thread_ref.as_id); +} + +ActivityTrackerMemoryAllocator::ActivityTrackerMemoryAllocator( + PersistentMemoryAllocator* allocator, + uint32_t object_type, + uint32_t object_free_type, + size_t object_size, + size_t cache_size, + bool make_iterable) + : allocator_(allocator), + object_type_(object_type), + object_free_type_(object_free_type), + object_size_(object_size), + cache_size_(cache_size), + make_iterable_(make_iterable), + iterator_(allocator), + cache_values_(new Reference[cache_size]), + cache_used_(0) { + DCHECK(allocator); +} + +ActivityTrackerMemoryAllocator::~ActivityTrackerMemoryAllocator() = default; + +ActivityTrackerMemoryAllocator::Reference +ActivityTrackerMemoryAllocator::GetObjectReference() { + // First see if there is a cached value that can be returned. This is much + // faster than searching the memory system for free blocks. + while (cache_used_ > 0) { + Reference cached = cache_values_[--cache_used_]; + // Change the type of the cached object to the proper type and return it. + // If the type-change fails that means another thread has taken this from + // under us (via the search below) so ignore it and keep trying. Don't + // clear the memory because that was done when the type was made "free". + if (allocator_->ChangeType(cached, object_type_, object_free_type_, false)) + return cached; + } + + // Fetch the next "free" object from persistent memory. Rather than restart + // the iterator at the head each time and likely waste time going again + // through objects that aren't relevant, the iterator continues from where + // it last left off and is only reset when the end is reached. If the + // returned reference matches |last|, then it has wrapped without finding + // anything. + const Reference last = iterator_.GetLast(); + while (true) { + uint32_t type; + Reference found = iterator_.GetNext(&type); + if (found && type == object_free_type_) { + // Found a free object. Change it to the proper type and return it. If + // the type-change fails that means another thread has taken this from + // under us so ignore it and keep trying. + if (allocator_->ChangeType(found, object_type_, object_free_type_, false)) + return found; + } + if (found == last) { + // Wrapped. No desired object was found. + break; + } + if (!found) { + // Reached end; start over at the beginning. + iterator_.Reset(); + } + } + + // No free block was found so instead allocate a new one. + Reference allocated = allocator_->Allocate(object_size_, object_type_); + if (allocated && make_iterable_) + allocator_->MakeIterable(allocated); + return allocated; +} + +void ActivityTrackerMemoryAllocator::ReleaseObjectReference(Reference ref) { + // Mark object as free. + bool success = allocator_->ChangeType(ref, object_free_type_, object_type_, + /*clear=*/true); + DCHECK(success); + + // Add this reference to our "free" cache if there is space. If not, the type + // has still been changed to indicate that it is free so this (or another) + // thread can find it, albeit more slowly, using the iteration method above. + if (cache_used_ < cache_size_) + cache_values_[cache_used_++] = ref; +} + +// static +void Activity::FillFrom(Activity* activity, + const void* program_counter, + const void* origin, + Type type, + const ActivityData& data) { + activity->time_internal = base::TimeTicks::Now().ToInternalValue(); + activity->calling_address = reinterpret_cast(program_counter); + activity->origin_address = reinterpret_cast(origin); + activity->activity_type = type; + activity->data = data; + +#if (!defined(OS_NACL) && DCHECK_IS_ON()) || defined(ADDRESS_SANITIZER) + // Create a stacktrace from the current location and get the addresses for + // improved debuggability. + StackTrace stack_trace; + size_t stack_depth; + const void* const* stack_addrs = stack_trace.Addresses(&stack_depth); + // Copy the stack addresses, ignoring the first one (here). + size_t i; + for (i = 1; i < stack_depth && i < kActivityCallStackSize; ++i) { + activity->call_stack[i - 1] = reinterpret_cast(stack_addrs[i]); + } + activity->call_stack[i - 1] = 0; +#else + activity->call_stack[0] = 0; +#endif +} + +ActivityUserData::TypedValue::TypedValue() = default; +ActivityUserData::TypedValue::TypedValue(const TypedValue& other) = default; +ActivityUserData::TypedValue::~TypedValue() = default; + +StringPiece ActivityUserData::TypedValue::Get() const { + DCHECK_EQ(RAW_VALUE, type_); + return long_value_; +} + +StringPiece ActivityUserData::TypedValue::GetString() const { + DCHECK_EQ(STRING_VALUE, type_); + return long_value_; +} + +bool ActivityUserData::TypedValue::GetBool() const { + DCHECK_EQ(BOOL_VALUE, type_); + return short_value_ != 0; +} + +char ActivityUserData::TypedValue::GetChar() const { + DCHECK_EQ(CHAR_VALUE, type_); + return static_cast(short_value_); +} + +int64_t ActivityUserData::TypedValue::GetInt() const { + DCHECK_EQ(SIGNED_VALUE, type_); + return static_cast(short_value_); +} + +uint64_t ActivityUserData::TypedValue::GetUint() const { + DCHECK_EQ(UNSIGNED_VALUE, type_); + return static_cast(short_value_); +} + +StringPiece ActivityUserData::TypedValue::GetReference() const { + DCHECK_EQ(RAW_VALUE_REFERENCE, type_); + return ref_value_; +} + +StringPiece ActivityUserData::TypedValue::GetStringReference() const { + DCHECK_EQ(STRING_VALUE_REFERENCE, type_); + return ref_value_; +} + +// These are required because std::atomic is (currently) not a POD type and +// thus clang requires explicit out-of-line constructors and destructors even +// when they do nothing. +ActivityUserData::ValueInfo::ValueInfo() = default; +ActivityUserData::ValueInfo::ValueInfo(ValueInfo&&) = default; +ActivityUserData::ValueInfo::~ValueInfo() = default; +ActivityUserData::MemoryHeader::MemoryHeader() = default; +ActivityUserData::MemoryHeader::~MemoryHeader() = default; +ActivityUserData::FieldHeader::FieldHeader() = default; +ActivityUserData::FieldHeader::~FieldHeader() = default; + +ActivityUserData::ActivityUserData() : ActivityUserData(nullptr, 0, -1) {} + +ActivityUserData::ActivityUserData(void* memory, size_t size, int64_t pid) + : memory_(reinterpret_cast(memory)), + available_(RoundDownToAlignment(size, kMemoryAlignment)), + header_(reinterpret_cast(memory)), + orig_data_id(0), + orig_process_id(0), + orig_create_stamp(0) { + // It's possible that no user data is being stored. + if (!memory_) + return; + + static_assert(0 == sizeof(MemoryHeader) % kMemoryAlignment, "invalid header"); + DCHECK_LT(sizeof(MemoryHeader), available_); + if (header_->owner.data_id.load(std::memory_order_acquire) == 0) + header_->owner.Release_Initialize(pid); + memory_ += sizeof(MemoryHeader); + available_ -= sizeof(MemoryHeader); + + // Make a copy of identifying information for later comparison. + *const_cast(&orig_data_id) = + header_->owner.data_id.load(std::memory_order_acquire); + *const_cast(&orig_process_id) = header_->owner.process_id; + *const_cast(&orig_create_stamp) = header_->owner.create_stamp; + + // If there is already data present, load that. This allows the same class + // to be used for analysis through snapshots. + ImportExistingData(); +} + +ActivityUserData::~ActivityUserData() = default; + +bool ActivityUserData::CreateSnapshot(Snapshot* output_snapshot) const { + DCHECK(output_snapshot); + DCHECK(output_snapshot->empty()); + + // Find any new data that may have been added by an active instance of this + // class that is adding records. + ImportExistingData(); + + // Add all the values to the snapshot. + for (const auto& entry : values_) { + TypedValue value; + const size_t size = entry.second.size_ptr->load(std::memory_order_acquire); + value.type_ = entry.second.type; + DCHECK_GE(entry.second.extent, size); + + switch (entry.second.type) { + case RAW_VALUE: + case STRING_VALUE: + value.long_value_ = + std::string(reinterpret_cast(entry.second.memory), size); + break; + case RAW_VALUE_REFERENCE: + case STRING_VALUE_REFERENCE: { + ReferenceRecord* ref = + reinterpret_cast(entry.second.memory); + value.ref_value_ = StringPiece( + reinterpret_cast(static_cast(ref->address)), + static_cast(ref->size)); + } break; + case BOOL_VALUE: + case CHAR_VALUE: + value.short_value_ = *reinterpret_cast(entry.second.memory); + break; + case SIGNED_VALUE: + case UNSIGNED_VALUE: + value.short_value_ = *reinterpret_cast(entry.second.memory); + break; + case END_OF_VALUES: // Included for completeness purposes. + NOTREACHED(); + } + auto inserted = output_snapshot->insert( + std::make_pair(entry.second.name.as_string(), std::move(value))); + DCHECK(inserted.second); // True if inserted, false if existed. + } + + // Another import attempt will validate that the underlying memory has not + // been reused for another purpose. Entries added since the first import + // will be ignored here but will be returned if another snapshot is created. + ImportExistingData(); + if (!memory_) { + output_snapshot->clear(); + return false; + } + + // Successful snapshot. + return true; +} + +const void* ActivityUserData::GetBaseAddress() const { + // The |memory_| pointer advances as elements are written but the |header_| + // value is always at the start of the block so just return that. + return header_; +} + +void ActivityUserData::SetOwningProcessIdForTesting(int64_t pid, + int64_t stamp) { + if (!header_) + return; + header_->owner.SetOwningProcessIdForTesting(pid, stamp); +} + +// static +bool ActivityUserData::GetOwningProcessId(const void* memory, + int64_t* out_id, + int64_t* out_stamp) { + const MemoryHeader* header = reinterpret_cast(memory); + return OwningProcess::GetOwningProcessId(&header->owner, out_id, out_stamp); +} + +void ActivityUserData::Set(StringPiece name, + ValueType type, + const void* memory, + size_t size) { + DCHECK_GE(std::numeric_limits::max(), name.length()); + size = std::min(std::numeric_limits::max() - (kMemoryAlignment - 1), + size); + + // It's possible that no user data is being stored. + if (!memory_) + return; + + // The storage of a name is limited so use that limit during lookup. + if (name.length() > kMaxUserDataNameLength) + name.set(name.data(), kMaxUserDataNameLength); + + ValueInfo* info; + auto existing = values_.find(name); + if (existing != values_.end()) { + info = &existing->second; + } else { + // The name size is limited to what can be held in a single byte but + // because there are not alignment constraints on strings, it's set tight + // against the header. Its extent (the reserved space, even if it's not + // all used) is calculated so that, when pressed against the header, the + // following field will be aligned properly. + size_t name_size = name.length(); + size_t name_extent = + RoundUpToAlignment(sizeof(FieldHeader) + name_size, kMemoryAlignment) - + sizeof(FieldHeader); + size_t value_extent = RoundUpToAlignment(size, kMemoryAlignment); + + // The "base size" is the size of the header and (padded) string key. Stop + // now if there's not room enough for even this. + size_t base_size = sizeof(FieldHeader) + name_extent; + if (base_size > available_) + return; + + // The "full size" is the size for storing the entire value. + size_t full_size = std::min(base_size + value_extent, available_); + + // If the value is actually a single byte, see if it can be stuffed at the + // end of the name extent rather than wasting kMemoryAlignment bytes. + if (size == 1 && name_extent > name_size) { + full_size = base_size; + --name_extent; + --base_size; + } + + // Truncate the stored size to the amount of available memory. Stop now if + // there's not any room for even part of the value. + if (size != 0) { + size = std::min(full_size - base_size, size); + if (size == 0) + return; + } + + // Allocate a chunk of memory. + FieldHeader* header = reinterpret_cast(memory_); + memory_ += full_size; + available_ -= full_size; + + // Datafill the header and name records. Memory must be zeroed. The |type| + // is written last, atomically, to release all the other values. + DCHECK_EQ(END_OF_VALUES, header->type.load(std::memory_order_relaxed)); + DCHECK_EQ(0, header->value_size.load(std::memory_order_relaxed)); + header->name_size = static_cast(name_size); + header->record_size = full_size; + char* name_memory = reinterpret_cast(header) + sizeof(FieldHeader); + void* value_memory = + reinterpret_cast(header) + sizeof(FieldHeader) + name_extent; + memcpy(name_memory, name.data(), name_size); + header->type.store(type, std::memory_order_release); + + // Create an entry in |values_| so that this field can be found and changed + // later on without having to allocate new entries. + StringPiece persistent_name(name_memory, name_size); + auto inserted = + values_.insert(std::make_pair(persistent_name, ValueInfo())); + DCHECK(inserted.second); // True if inserted, false if existed. + info = &inserted.first->second; + info->name = persistent_name; + info->memory = value_memory; + info->size_ptr = &header->value_size; + info->extent = full_size - sizeof(FieldHeader) - name_extent; + info->type = type; + } + + // Copy the value data to storage. The |size| is written last, atomically, to + // release the copied data. Until then, a parallel reader will just ignore + // records with a zero size. + DCHECK_EQ(type, info->type); + size = std::min(size, info->extent); + info->size_ptr->store(0, std::memory_order_seq_cst); + memcpy(info->memory, memory, size); + info->size_ptr->store(size, std::memory_order_release); +} + +void ActivityUserData::SetReference(StringPiece name, + ValueType type, + const void* memory, + size_t size) { + ReferenceRecord rec; + rec.address = reinterpret_cast(memory); + rec.size = size; + Set(name, type, &rec, sizeof(rec)); +} + +void ActivityUserData::ImportExistingData() const { + // It's possible that no user data is being stored. + if (!memory_) + return; + + while (available_ > sizeof(FieldHeader)) { + FieldHeader* header = reinterpret_cast(memory_); + ValueType type = + static_cast(header->type.load(std::memory_order_acquire)); + if (type == END_OF_VALUES) + return; + if (header->record_size > available_) + return; + + size_t value_offset = RoundUpToAlignment( + sizeof(FieldHeader) + header->name_size, kMemoryAlignment); + if (header->record_size == value_offset && + header->value_size.load(std::memory_order_relaxed) == 1) { + value_offset -= 1; + } + if (value_offset + header->value_size > header->record_size) + return; + + ValueInfo info; + info.name = StringPiece(memory_ + sizeof(FieldHeader), header->name_size); + info.type = type; + info.memory = memory_ + value_offset; + info.size_ptr = &header->value_size; + info.extent = header->record_size - value_offset; + + StringPiece key(info.name); + values_.insert(std::make_pair(key, std::move(info))); + + memory_ += header->record_size; + available_ -= header->record_size; + } + + // Check if memory has been completely reused. + if (header_->owner.data_id.load(std::memory_order_acquire) != orig_data_id || + header_->owner.process_id != orig_process_id || + header_->owner.create_stamp != orig_create_stamp) { + memory_ = nullptr; + values_.clear(); + } +} + +// This information is kept for every thread that is tracked. It is filled +// the very first time the thread is seen. All fields must be of exact sizes +// so there is no issue moving between 32 and 64-bit builds. +struct ThreadActivityTracker::Header { + // Defined in .h for analyzer access. Increment this if structure changes! + static constexpr uint32_t kPersistentTypeId = + GlobalActivityTracker::kTypeIdActivityTracker; + + // Expected size for 32/64-bit check. + static constexpr size_t kExpectedInstanceSize = + OwningProcess::kExpectedInstanceSize + Activity::kExpectedInstanceSize + + 72; + + // This information uniquely identifies a process. + OwningProcess owner; + + // The thread-id (thread_ref.as_id) to which this data belongs. This number + // is not guaranteed to mean anything but combined with the process-id from + // OwningProcess is unique among all active trackers. + ThreadRef thread_ref; + + // The start-time and start-ticks when the data was created. Each activity + // record has a |time_internal| value that can be converted to a "wall time" + // with these two values. + int64_t start_time; + int64_t start_ticks; + + // The number of Activity slots (spaces that can hold an Activity) that + // immediately follow this structure in memory. + uint32_t stack_slots; + + // Some padding to keep everything 64-bit aligned. + uint32_t padding; + + // The current depth of the stack. This may be greater than the number of + // slots. If the depth exceeds the number of slots, the newest entries + // won't be recorded. + std::atomic current_depth; + + // A memory location used to indicate if changes have been made to the data + // that would invalidate an in-progress read of its contents. The active + // tracker will increment the value whenever something gets popped from the + // stack. A monitoring tracker can check the value before and after access + // to know, if it's still the same, that the contents didn't change while + // being copied. + std::atomic data_version; + + // The last "exception" activity. This can't be stored on the stack because + // that could get popped as things unwind. + Activity last_exception; + + // The name of the thread (up to a maximum length). Dynamic-length names + // are not practical since the memory has to come from the same persistent + // allocator that holds this structure and to which this object has no + // reference. + char thread_name[32]; +}; + +ThreadActivityTracker::Snapshot::Snapshot() = default; +ThreadActivityTracker::Snapshot::~Snapshot() = default; + +ThreadActivityTracker::ScopedActivity::ScopedActivity( + ThreadActivityTracker* tracker, + const void* program_counter, + const void* origin, + Activity::Type type, + const ActivityData& data) + : tracker_(tracker) { + if (tracker_) + activity_id_ = tracker_->PushActivity(program_counter, origin, type, data); +} + +ThreadActivityTracker::ScopedActivity::~ScopedActivity() { + if (tracker_) + tracker_->PopActivity(activity_id_); +} + +void ThreadActivityTracker::ScopedActivity::ChangeTypeAndData( + Activity::Type type, + const ActivityData& data) { + if (tracker_) + tracker_->ChangeActivity(activity_id_, type, data); +} + +ThreadActivityTracker::ThreadActivityTracker(void* base, size_t size) + : header_(static_cast(base)), + stack_(reinterpret_cast(reinterpret_cast(base) + + sizeof(Header))), +#if DCHECK_IS_ON() + thread_id_(PlatformThreadRef()), +#endif + stack_slots_( + static_cast((size - sizeof(Header)) / sizeof(Activity))) { + + // Verify the parameters but fail gracefully if they're not valid so that + // production code based on external inputs will not crash. IsValid() will + // return false in this case. + if (!base || + // Ensure there is enough space for the header and at least a few records. + size < sizeof(Header) + kMinStackDepth * sizeof(Activity) || + // Ensure that the |stack_slots_| calculation didn't overflow. + (size - sizeof(Header)) / sizeof(Activity) > + std::numeric_limits::max()) { + NOTREACHED(); + return; + } + + // Ensure that the thread reference doesn't exceed the size of the ID number. + // This won't compile at the global scope because Header is a private struct. + static_assert( + sizeof(header_->thread_ref) == sizeof(header_->thread_ref.as_id), + "PlatformThreadHandle::Handle is too big to hold in 64-bit ID"); + + // Ensure that the alignment of Activity.data is properly aligned to a + // 64-bit boundary so there are no interoperability-issues across cpu + // architectures. + static_assert(offsetof(Activity, data) % sizeof(uint64_t) == 0, + "ActivityData.data is not 64-bit aligned"); + + // Provided memory should either be completely initialized or all zeros. + if (header_->owner.data_id.load(std::memory_order_relaxed) == 0) { + // This is a new file. Double-check other fields and then initialize. + DCHECK_EQ(0, header_->owner.process_id); + DCHECK_EQ(0, header_->owner.create_stamp); + DCHECK_EQ(0, header_->thread_ref.as_id); + DCHECK_EQ(0, header_->start_time); + DCHECK_EQ(0, header_->start_ticks); + DCHECK_EQ(0U, header_->stack_slots); + DCHECK_EQ(0U, header_->current_depth.load(std::memory_order_relaxed)); + DCHECK_EQ(0U, header_->data_version.load(std::memory_order_relaxed)); + DCHECK_EQ(0, stack_[0].time_internal); + DCHECK_EQ(0U, stack_[0].origin_address); + DCHECK_EQ(0U, stack_[0].call_stack[0]); + DCHECK_EQ(0U, stack_[0].data.task.sequence_id); + +#if defined(OS_WIN) + header_->thread_ref.as_tid = PlatformThread::CurrentId(); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + header_->thread_ref.as_handle = + PlatformThread::CurrentHandle().platform_handle(); +#endif + + header_->start_time = base::Time::Now().ToInternalValue(); + header_->start_ticks = base::TimeTicks::Now().ToInternalValue(); + header_->stack_slots = stack_slots_; + strlcpy(header_->thread_name, PlatformThread::GetName(), + sizeof(header_->thread_name)); + + // This is done last so as to guarantee that everything above is "released" + // by the time this value gets written. + header_->owner.Release_Initialize(); + + valid_ = true; + DCHECK(IsValid()); + } else { + // This is a file with existing data. Perform basic consistency checks. + valid_ = true; + valid_ = IsValid(); + } +} + +ThreadActivityTracker::~ThreadActivityTracker() = default; + +ThreadActivityTracker::ActivityId ThreadActivityTracker::PushActivity( + const void* program_counter, + const void* origin, + Activity::Type type, + const ActivityData& data) { + // A thread-checker creates a lock to check the thread-id which means + // re-entry into this code if lock acquisitions are being tracked. + DCHECK(type == Activity::ACT_LOCK_ACQUIRE || CalledOnValidThread()); + + // Get the current depth of the stack. No access to other memory guarded + // by this variable is done here so a "relaxed" load is acceptable. + uint32_t depth = header_->current_depth.load(std::memory_order_relaxed); + + // Handle the case where the stack depth has exceeded the storage capacity. + // Extra entries will be lost leaving only the base of the stack. + if (depth >= stack_slots_) { + // Since no other threads modify the data, no compare/exchange is needed. + // Since no other memory is being modified, a "relaxed" store is acceptable. + header_->current_depth.store(depth + 1, std::memory_order_relaxed); + return depth; + } + + // Get a pointer to the next activity and load it. No atomicity is required + // here because the memory is known only to this thread. It will be made + // known to other threads once the depth is incremented. + Activity::FillFrom(&stack_[depth], program_counter, origin, type, data); + + // Save the incremented depth. Because this guards |activity| memory filled + // above that may be read by another thread once the recorded depth changes, + // a "release" store is required. + header_->current_depth.store(depth + 1, std::memory_order_release); + + // The current depth is used as the activity ID because it simply identifies + // an entry. Once an entry is pop'd, it's okay to reuse the ID. + return depth; +} + +void ThreadActivityTracker::ChangeActivity(ActivityId id, + Activity::Type type, + const ActivityData& data) { + DCHECK(CalledOnValidThread()); + DCHECK(type != Activity::ACT_NULL || &data != &kNullActivityData); + DCHECK_LT(id, header_->current_depth.load(std::memory_order_acquire)); + + // Update the information if it is being recorded (i.e. within slot limit). + if (id < stack_slots_) { + Activity* activity = &stack_[id]; + + if (type != Activity::ACT_NULL) { + DCHECK_EQ(activity->activity_type & Activity::ACT_CATEGORY_MASK, + type & Activity::ACT_CATEGORY_MASK); + activity->activity_type = type; + } + + if (&data != &kNullActivityData) + activity->data = data; + } +} + +void ThreadActivityTracker::PopActivity(ActivityId id) { + // Do an atomic decrement of the depth. No changes to stack entries guarded + // by this variable are done here so a "relaxed" operation is acceptable. + // |depth| will receive the value BEFORE it was modified which means the + // return value must also be decremented. The slot will be "free" after + // this call but since only a single thread can access this object, the + // data will remain valid until this method returns or calls outside. + uint32_t depth = + header_->current_depth.fetch_sub(1, std::memory_order_relaxed) - 1; + + // Validate that everything is running correctly. + DCHECK_EQ(id, depth); + + // A thread-checker creates a lock to check the thread-id which means + // re-entry into this code if lock acquisitions are being tracked. + DCHECK(stack_[depth].activity_type == Activity::ACT_LOCK_ACQUIRE || + CalledOnValidThread()); + + // The stack has shrunk meaning that some other thread trying to copy the + // contents for reporting purposes could get bad data. Increment the data + // version so that it con tell that things have changed. This needs to + // happen after the atomic |depth| operation above so a "release" store + // is required. + header_->data_version.fetch_add(1, std::memory_order_release); +} + +std::unique_ptr ThreadActivityTracker::GetUserData( + ActivityId id, + ActivityTrackerMemoryAllocator* allocator) { + // Don't allow user data for lock acquisition as recursion may occur. + if (stack_[id].activity_type == Activity::ACT_LOCK_ACQUIRE) { + NOTREACHED(); + return std::make_unique(); + } + + // User-data is only stored for activities actually held in the stack. + if (id >= stack_slots_) + return std::make_unique(); + + // Create and return a real UserData object. + return CreateUserDataForActivity(&stack_[id], allocator); +} + +bool ThreadActivityTracker::HasUserData(ActivityId id) { + // User-data is only stored for activities actually held in the stack. + return (id < stack_slots_ && stack_[id].user_data_ref); +} + +void ThreadActivityTracker::ReleaseUserData( + ActivityId id, + ActivityTrackerMemoryAllocator* allocator) { + // User-data is only stored for activities actually held in the stack. + if (id < stack_slots_ && stack_[id].user_data_ref) { + allocator->ReleaseObjectReference(stack_[id].user_data_ref); + stack_[id].user_data_ref = 0; + } +} + +void ThreadActivityTracker::RecordExceptionActivity(const void* program_counter, + const void* origin, + Activity::Type type, + const ActivityData& data) { + // A thread-checker creates a lock to check the thread-id which means + // re-entry into this code if lock acquisitions are being tracked. + DCHECK(CalledOnValidThread()); + + // Fill the reusable exception activity. + Activity::FillFrom(&header_->last_exception, program_counter, origin, type, + data); + + // The data has changed meaning that some other thread trying to copy the + // contents for reporting purposes could get bad data. + header_->data_version.fetch_add(1, std::memory_order_relaxed); +} + +bool ThreadActivityTracker::IsValid() const { + if (header_->owner.data_id.load(std::memory_order_acquire) == 0 || + header_->owner.process_id == 0 || header_->thread_ref.as_id == 0 || + header_->start_time == 0 || header_->start_ticks == 0 || + header_->stack_slots != stack_slots_ || + header_->thread_name[sizeof(header_->thread_name) - 1] != '\0') { + return false; + } + + return valid_; +} + +bool ThreadActivityTracker::CreateSnapshot(Snapshot* output_snapshot) const { + DCHECK(output_snapshot); + + // There is no "called on valid thread" check for this method as it can be + // called from other threads or even other processes. It is also the reason + // why atomic operations must be used in certain places above. + + // It's possible for the data to change while reading it in such a way that it + // invalidates the read. Make several attempts but don't try forever. + const int kMaxAttempts = 10; + uint32_t depth; + + // Stop here if the data isn't valid. + if (!IsValid()) + return false; + + // Allocate the maximum size for the stack so it doesn't have to be done + // during the time-sensitive snapshot operation. It is shrunk once the + // actual size is known. + output_snapshot->activity_stack.reserve(stack_slots_); + + for (int attempt = 0; attempt < kMaxAttempts; ++attempt) { + // Remember the data IDs to ensure nothing is replaced during the snapshot + // operation. Use "acquire" so that all the non-atomic fields of the + // structure are valid (at least at the current moment in time). + const uint32_t starting_id = + header_->owner.data_id.load(std::memory_order_acquire); + const int64_t starting_create_stamp = header_->owner.create_stamp; + const int64_t starting_process_id = header_->owner.process_id; + const int64_t starting_thread_id = header_->thread_ref.as_id; + + // Note the current |data_version| so it's possible to detect at the end + // that nothing has changed since copying the data began. A "cst" operation + // is required to ensure it occurs before everything else. Using "cst" + // memory ordering is relatively expensive but this is only done during + // analysis so doesn't directly affect the worker threads. + const uint32_t pre_version = + header_->data_version.load(std::memory_order_seq_cst); + + // Fetching the current depth also "acquires" the contents of the stack. + depth = header_->current_depth.load(std::memory_order_acquire); + uint32_t count = std::min(depth, stack_slots_); + output_snapshot->activity_stack.resize(count); + if (count > 0) { + // Copy the existing contents. Memcpy is used for speed. + memcpy(&output_snapshot->activity_stack[0], stack_, + count * sizeof(Activity)); + } + + // Capture the last exception. + memcpy(&output_snapshot->last_exception, &header_->last_exception, + sizeof(Activity)); + + // TODO(bcwhite): Snapshot other things here. + + // Retry if something changed during the copy. A "cst" operation ensures + // it must happen after all the above operations. + if (header_->data_version.load(std::memory_order_seq_cst) != pre_version) + continue; + + // Stack copied. Record it's full depth. + output_snapshot->activity_stack_depth = depth; + + // Get the general thread information. + output_snapshot->thread_name = + std::string(header_->thread_name, sizeof(header_->thread_name) - 1); + output_snapshot->create_stamp = header_->owner.create_stamp; + output_snapshot->thread_id = header_->thread_ref.as_id; + output_snapshot->process_id = header_->owner.process_id; + + // All characters of the thread-name buffer were copied so as to not break + // if the trailing NUL were missing. Now limit the length if the actual + // name is shorter. + output_snapshot->thread_name.resize( + strlen(output_snapshot->thread_name.c_str())); + + // If the data ID has changed then the tracker has exited and the memory + // reused by a new one. Try again. + if (header_->owner.data_id.load(std::memory_order_seq_cst) != starting_id || + output_snapshot->create_stamp != starting_create_stamp || + output_snapshot->process_id != starting_process_id || + output_snapshot->thread_id != starting_thread_id) { + continue; + } + + // Only successful if the data is still valid once everything is done since + // it's possible for the thread to end somewhere in the middle and all its + // values become garbage. + if (!IsValid()) + return false; + + // Change all the timestamps in the activities from "ticks" to "wall" time. + const Time start_time = Time::FromInternalValue(header_->start_time); + const int64_t start_ticks = header_->start_ticks; + for (Activity& activity : output_snapshot->activity_stack) { + activity.time_internal = + WallTimeFromTickTime(start_ticks, activity.time_internal, start_time) + .ToInternalValue(); + } + output_snapshot->last_exception.time_internal = + WallTimeFromTickTime(start_ticks, + output_snapshot->last_exception.time_internal, + start_time) + .ToInternalValue(); + + // Success! + return true; + } + + // Too many attempts. + return false; +} + +const void* ThreadActivityTracker::GetBaseAddress() { + return header_; +} + +uint32_t ThreadActivityTracker::GetDataVersionForTesting() { + return header_->data_version.load(std::memory_order_relaxed); +} + +void ThreadActivityTracker::SetOwningProcessIdForTesting(int64_t pid, + int64_t stamp) { + header_->owner.SetOwningProcessIdForTesting(pid, stamp); +} + +// static +bool ThreadActivityTracker::GetOwningProcessId(const void* memory, + int64_t* out_id, + int64_t* out_stamp) { + const Header* header = reinterpret_cast(memory); + return OwningProcess::GetOwningProcessId(&header->owner, out_id, out_stamp); +} + +// static +size_t ThreadActivityTracker::SizeForStackDepth(int stack_depth) { + return static_cast(stack_depth) * sizeof(Activity) + sizeof(Header); +} + +bool ThreadActivityTracker::CalledOnValidThread() { +#if DCHECK_IS_ON() + return thread_id_ == PlatformThreadRef(); +#else + return true; +#endif +} + +std::unique_ptr +ThreadActivityTracker::CreateUserDataForActivity( + Activity* activity, + ActivityTrackerMemoryAllocator* allocator) { + DCHECK_EQ(0U, activity->user_data_ref); + + PersistentMemoryAllocator::Reference ref = allocator->GetObjectReference(); + void* memory = allocator->GetAsArray(ref, kUserDataSize); + if (memory) { + std::unique_ptr user_data = + std::make_unique(memory, kUserDataSize); + activity->user_data_ref = ref; + activity->user_data_id = user_data->id(); + return user_data; + } + + // Return a dummy object that will still accept (but ignore) Set() calls. + return std::make_unique(); +} + +// The instantiation of the GlobalActivityTracker object. +// The object held here will obviously not be destructed at process exit +// but that's best since PersistentMemoryAllocator objects (that underlie +// GlobalActivityTracker objects) are explicitly forbidden from doing anything +// essential at exit anyway due to the fact that they depend on data managed +// elsewhere and which could be destructed first. An AtomicWord is used instead +// of std::atomic because the latter can create global ctors and dtors. +subtle::AtomicWord GlobalActivityTracker::g_tracker_ = 0; + +GlobalActivityTracker::ModuleInfo::ModuleInfo() = default; +GlobalActivityTracker::ModuleInfo::ModuleInfo(ModuleInfo&& rhs) = default; +GlobalActivityTracker::ModuleInfo::ModuleInfo(const ModuleInfo& rhs) = default; +GlobalActivityTracker::ModuleInfo::~ModuleInfo() = default; + +GlobalActivityTracker::ModuleInfo& GlobalActivityTracker::ModuleInfo::operator=( + ModuleInfo&& rhs) = default; +GlobalActivityTracker::ModuleInfo& GlobalActivityTracker::ModuleInfo::operator=( + const ModuleInfo& rhs) = default; + +GlobalActivityTracker::ModuleInfoRecord::ModuleInfoRecord() = default; +GlobalActivityTracker::ModuleInfoRecord::~ModuleInfoRecord() = default; + +bool GlobalActivityTracker::ModuleInfoRecord::DecodeTo( + GlobalActivityTracker::ModuleInfo* info, + size_t record_size) const { + // Get the current "changes" indicator, acquiring all the other values. + uint32_t current_changes = changes.load(std::memory_order_acquire); + + // Copy out the dynamic information. + info->is_loaded = loaded != 0; + info->address = static_cast(address); + info->load_time = load_time; + + // Check to make sure no information changed while being read. A "seq-cst" + // operation is expensive but is only done during analysis and it's the only + // way to ensure this occurs after all the accesses above. If changes did + // occur then return a "not loaded" result so that |size| and |address| + // aren't expected to be accurate. + if ((current_changes & kModuleInformationChanging) != 0 || + changes.load(std::memory_order_seq_cst) != current_changes) { + info->is_loaded = false; + } + + // Copy out the static information. These never change so don't have to be + // protected by the atomic |current_changes| operations. + info->size = static_cast(size); + info->timestamp = timestamp; + info->age = age; + memcpy(info->identifier, identifier, sizeof(info->identifier)); + + if (offsetof(ModuleInfoRecord, pickle) + pickle_size > record_size) + return false; + Pickle pickler(pickle, pickle_size); + PickleIterator iter(pickler); + return iter.ReadString(&info->file) && iter.ReadString(&info->debug_file); +} + +GlobalActivityTracker::ModuleInfoRecord* +GlobalActivityTracker::ModuleInfoRecord::CreateFrom( + const GlobalActivityTracker::ModuleInfo& info, + PersistentMemoryAllocator* allocator) { + Pickle pickler; + pickler.WriteString(info.file); + pickler.WriteString(info.debug_file); + size_t required_size = offsetof(ModuleInfoRecord, pickle) + pickler.size(); + ModuleInfoRecord* record = allocator->New(required_size); + if (!record) + return nullptr; + + // These fields never changes and are done before the record is made + // iterable so no thread protection is necessary. + record->size = info.size; + record->timestamp = info.timestamp; + record->age = info.age; + memcpy(record->identifier, info.identifier, sizeof(identifier)); + memcpy(record->pickle, pickler.data(), pickler.size()); + record->pickle_size = pickler.size(); + record->changes.store(0, std::memory_order_relaxed); + + // Initialize the owner info. + record->owner.Release_Initialize(); + + // Now set those fields that can change. + bool success = record->UpdateFrom(info); + DCHECK(success); + return record; +} + +bool GlobalActivityTracker::ModuleInfoRecord::UpdateFrom( + const GlobalActivityTracker::ModuleInfo& info) { + // Updates can occur after the record is made visible so make changes atomic. + // A "strong" exchange ensures no false failures. + uint32_t old_changes = changes.load(std::memory_order_relaxed); + uint32_t new_changes = old_changes | kModuleInformationChanging; + if ((old_changes & kModuleInformationChanging) != 0 || + !changes.compare_exchange_strong(old_changes, new_changes, + std::memory_order_acquire, + std::memory_order_acquire)) { + NOTREACHED() << "Multiple sources are updating module information."; + return false; + } + + loaded = info.is_loaded ? 1 : 0; + address = info.address; + load_time = Time::Now().ToInternalValue(); + + bool success = changes.compare_exchange_strong(new_changes, old_changes + 1, + std::memory_order_release, + std::memory_order_relaxed); + DCHECK(success); + return true; +} + +GlobalActivityTracker::ScopedThreadActivity::ScopedThreadActivity( + const void* program_counter, + const void* origin, + Activity::Type type, + const ActivityData& data, + bool lock_allowed) + : ThreadActivityTracker::ScopedActivity(GetOrCreateTracker(lock_allowed), + program_counter, + origin, + type, + data) {} + +GlobalActivityTracker::ScopedThreadActivity::~ScopedThreadActivity() { + if (tracker_ && tracker_->HasUserData(activity_id_)) { + GlobalActivityTracker* global = GlobalActivityTracker::Get(); + AutoLock lock(global->user_data_allocator_lock_); + tracker_->ReleaseUserData(activity_id_, &global->user_data_allocator_); + } +} + +ActivityUserData& GlobalActivityTracker::ScopedThreadActivity::user_data() { + if (!user_data_) { + if (tracker_) { + GlobalActivityTracker* global = GlobalActivityTracker::Get(); + AutoLock lock(global->user_data_allocator_lock_); + user_data_ = + tracker_->GetUserData(activity_id_, &global->user_data_allocator_); + } else { + user_data_ = std::make_unique(); + } + } + return *user_data_; +} + +GlobalActivityTracker::ThreadSafeUserData::ThreadSafeUserData(void* memory, + size_t size, + int64_t pid) + : ActivityUserData(memory, size, pid) {} + +GlobalActivityTracker::ThreadSafeUserData::~ThreadSafeUserData() = default; + +void GlobalActivityTracker::ThreadSafeUserData::Set(StringPiece name, + ValueType type, + const void* memory, + size_t size) { + AutoLock lock(data_lock_); + ActivityUserData::Set(name, type, memory, size); +} + +GlobalActivityTracker::ManagedActivityTracker::ManagedActivityTracker( + PersistentMemoryAllocator::Reference mem_reference, + void* base, + size_t size) + : ThreadActivityTracker(base, size), + mem_reference_(mem_reference), + mem_base_(base) {} + +GlobalActivityTracker::ManagedActivityTracker::~ManagedActivityTracker() { + // The global |g_tracker_| must point to the owner of this class since all + // objects of this type must be destructed before |g_tracker_| can be changed + // (something that only occurs in tests). + DCHECK(g_tracker_); + GlobalActivityTracker::Get()->ReturnTrackerMemory(this); +} + +void GlobalActivityTracker::CreateWithAllocator( + std::unique_ptr allocator, + int stack_depth, + int64_t process_id) { + // There's no need to do anything with the result. It is self-managing. + GlobalActivityTracker* global_tracker = + new GlobalActivityTracker(std::move(allocator), stack_depth, process_id); + // Create a tracker for this thread since it is known. + global_tracker->CreateTrackerForCurrentThread(); +} + +#if !defined(OS_NACL) +// static +bool GlobalActivityTracker::CreateWithFile(const FilePath& file_path, + size_t size, + uint64_t id, + StringPiece name, + int stack_depth) { + DCHECK(!file_path.empty()); + DCHECK_GE(static_cast(std::numeric_limits::max()), size); + + // Create and map the file into memory and make it globally available. + std::unique_ptr mapped_file(new MemoryMappedFile()); + bool success = mapped_file->Initialize( + File(file_path, File::FLAG_CREATE_ALWAYS | File::FLAG_READ | + File::FLAG_WRITE | File::FLAG_SHARE_DELETE), + {0, size}, MemoryMappedFile::READ_WRITE_EXTEND); + if (!success) + return false; + if (!FilePersistentMemoryAllocator::IsFileAcceptable(*mapped_file, false)) + return false; + CreateWithAllocator(std::make_unique( + std::move(mapped_file), size, id, name, false), + stack_depth, 0); + return true; +} +#endif // !defined(OS_NACL) + +// static +bool GlobalActivityTracker::CreateWithLocalMemory(size_t size, + uint64_t id, + StringPiece name, + int stack_depth, + int64_t process_id) { + CreateWithAllocator( + std::make_unique(size, id, name), + stack_depth, process_id); + return true; +} + +// static +bool GlobalActivityTracker::CreateWithSharedMemory( + std::unique_ptr shm, + uint64_t id, + StringPiece name, + int stack_depth) { + if (shm->mapped_size() == 0 || + !SharedPersistentMemoryAllocator::IsSharedMemoryAcceptable(*shm)) { + return false; + } + CreateWithAllocator(std::make_unique( + std::move(shm), id, name, false), + stack_depth, 0); + return true; +} + +// static +bool GlobalActivityTracker::CreateWithSharedMemoryHandle( + const SharedMemoryHandle& handle, + size_t size, + uint64_t id, + StringPiece name, + int stack_depth) { + std::unique_ptr shm( + new SharedMemory(handle, /*readonly=*/false)); + if (!shm->Map(size)) + return false; + return CreateWithSharedMemory(std::move(shm), id, name, stack_depth); +} + +// static +void GlobalActivityTracker::SetForTesting( + std::unique_ptr tracker) { + CHECK(!subtle::NoBarrier_Load(&g_tracker_)); + subtle::Release_Store(&g_tracker_, + reinterpret_cast(tracker.release())); +} + +// static +std::unique_ptr +GlobalActivityTracker::ReleaseForTesting() { + GlobalActivityTracker* tracker = Get(); + if (!tracker) + return nullptr; + + // Thread trackers assume that the global tracker is present for some + // operations so ensure that there aren't any. + tracker->ReleaseTrackerForCurrentThreadForTesting(); + DCHECK_EQ(0, tracker->thread_tracker_count_.load(std::memory_order_relaxed)); + + subtle::Release_Store(&g_tracker_, 0); + return WrapUnique(tracker); +} + +ThreadActivityTracker* GlobalActivityTracker::CreateTrackerForCurrentThread() { + DCHECK(!this_thread_tracker_.Get()); + + PersistentMemoryAllocator::Reference mem_reference; + + { + base::AutoLock autolock(thread_tracker_allocator_lock_); + mem_reference = thread_tracker_allocator_.GetObjectReference(); + } + + if (!mem_reference) { + // Failure. This shouldn't happen. But be graceful if it does, probably + // because the underlying allocator wasn't given enough memory to satisfy + // to all possible requests. + NOTREACHED(); + // Report the thread-count at which the allocator was full so that the + // failure can be seen and underlying memory resized appropriately. + UMA_HISTOGRAM_COUNTS_1000( + "ActivityTracker.ThreadTrackers.MemLimitTrackerCount", + thread_tracker_count_.load(std::memory_order_relaxed)); + // Return null, just as if tracking wasn't enabled. + return nullptr; + } + + // Convert the memory block found above into an actual memory address. + // Doing the conversion as a Header object enacts the 32/64-bit size + // consistency checks which would not otherwise be done. Unfortunately, + // some older compilers and MSVC don't have standard-conforming definitions + // of std::atomic which cause it not to be plain-old-data. Don't check on + // those platforms assuming that the checks on other platforms will be + // sufficient. + // TODO(bcwhite): Review this after major compiler releases. + DCHECK(mem_reference); + void* mem_base; + mem_base = + allocator_->GetAsObject(mem_reference); + + DCHECK(mem_base); + DCHECK_LE(stack_memory_size_, allocator_->GetAllocSize(mem_reference)); + + // Create a tracker with the acquired memory and set it as the tracker + // for this particular thread in thread-local-storage. + ManagedActivityTracker* tracker = + new ManagedActivityTracker(mem_reference, mem_base, stack_memory_size_); + DCHECK(tracker->IsValid()); + this_thread_tracker_.Set(tracker); + int old_count = thread_tracker_count_.fetch_add(1, std::memory_order_relaxed); + + UMA_HISTOGRAM_EXACT_LINEAR("ActivityTracker.ThreadTrackers.Count", + old_count + 1, static_cast(kMaxThreadCount)); + return tracker; +} + +void GlobalActivityTracker::ReleaseTrackerForCurrentThreadForTesting() { + ThreadActivityTracker* tracker = + reinterpret_cast(this_thread_tracker_.Get()); + if (tracker) { + this_thread_tracker_.Set(nullptr); + delete tracker; + } +} + +void GlobalActivityTracker::SetBackgroundTaskRunner( + const scoped_refptr& runner) { + AutoLock lock(global_tracker_lock_); + background_task_runner_ = runner; +} + +void GlobalActivityTracker::SetProcessExitCallback( + ProcessExitCallback callback) { + AutoLock lock(global_tracker_lock_); + process_exit_callback_ = callback; +} + +void GlobalActivityTracker::RecordProcessLaunch( + ProcessId process_id, + const FilePath::StringType& cmd) { + const int64_t pid = process_id; + DCHECK_NE(GetProcessId(), pid); + DCHECK_NE(0, pid); + + base::AutoLock lock(global_tracker_lock_); + if (base::ContainsKey(known_processes_, pid)) { + // TODO(bcwhite): Measure this in UMA. + NOTREACHED() << "Process #" << process_id + << " was previously recorded as \"launched\"" + << " with no corresponding exit.\n" + << known_processes_[pid]; + known_processes_.erase(pid); + } + +#if defined(OS_WIN) + known_processes_.insert(std::make_pair(pid, UTF16ToUTF8(cmd))); +#else + known_processes_.insert(std::make_pair(pid, cmd)); +#endif +} + +void GlobalActivityTracker::RecordProcessLaunch( + ProcessId process_id, + const FilePath::StringType& exe, + const FilePath::StringType& args) { + if (exe.find(FILE_PATH_LITERAL(" "))) { + RecordProcessLaunch(process_id, + FilePath::StringType(FILE_PATH_LITERAL("\"")) + exe + + FILE_PATH_LITERAL("\" ") + args); + } else { + RecordProcessLaunch(process_id, exe + FILE_PATH_LITERAL(' ') + args); + } +} + +void GlobalActivityTracker::RecordProcessExit(ProcessId process_id, + int exit_code) { + const int64_t pid = process_id; + DCHECK_NE(GetProcessId(), pid); + DCHECK_NE(0, pid); + + scoped_refptr task_runner; + std::string command_line; + { + base::AutoLock lock(global_tracker_lock_); + task_runner = background_task_runner_; + auto found = known_processes_.find(pid); + if (found != known_processes_.end()) { + command_line = std::move(found->second); + known_processes_.erase(found); + } else { + DLOG(ERROR) << "Recording exit of unknown process #" << process_id; + } + } + + // Use the current time to differentiate the process that just exited + // from any that might be created in the future with the same ID. + int64_t now_stamp = Time::Now().ToInternalValue(); + + // The persistent allocator is thread-safe so run the iteration and + // adjustments on a worker thread if one was provided. + if (task_runner && !task_runner->RunsTasksInCurrentSequence()) { + task_runner->PostTask( + FROM_HERE, + BindOnce(&GlobalActivityTracker::CleanupAfterProcess, Unretained(this), + pid, now_stamp, exit_code, std::move(command_line))); + return; + } + + CleanupAfterProcess(pid, now_stamp, exit_code, std::move(command_line)); +} + +void GlobalActivityTracker::SetProcessPhase(ProcessPhase phase) { + process_data().SetInt(kProcessPhaseDataKey, phase); +} + +void GlobalActivityTracker::CleanupAfterProcess(int64_t process_id, + int64_t exit_stamp, + int exit_code, + std::string&& command_line) { + // The process may not have exited cleanly so its necessary to go through + // all the data structures it may have allocated in the persistent memory + // segment and mark them as "released". This will allow them to be reused + // later on. + + PersistentMemoryAllocator::Iterator iter(allocator_.get()); + PersistentMemoryAllocator::Reference ref; + + ProcessExitCallback process_exit_callback; + { + AutoLock lock(global_tracker_lock_); + process_exit_callback = process_exit_callback_; + } + if (process_exit_callback) { + // Find the processes user-data record so the process phase can be passed + // to the callback. + ActivityUserData::Snapshot process_data_snapshot; + while ((ref = iter.GetNextOfType(kTypeIdProcessDataRecord)) != 0) { + const void* memory = allocator_->GetAsArray( + ref, kTypeIdProcessDataRecord, PersistentMemoryAllocator::kSizeAny); + if (!memory) + continue; + int64_t found_id; + int64_t create_stamp; + if (ActivityUserData::GetOwningProcessId(memory, &found_id, + &create_stamp)) { + if (found_id == process_id && create_stamp < exit_stamp) { + const ActivityUserData process_data(const_cast(memory), + allocator_->GetAllocSize(ref)); + process_data.CreateSnapshot(&process_data_snapshot); + break; // No need to look for any others. + } + } + } + iter.Reset(); // So it starts anew when used below. + + // Record the process's phase at exit so callback doesn't need to go + // searching based on a private key value. + ProcessPhase exit_phase = PROCESS_PHASE_UNKNOWN; + auto phase = process_data_snapshot.find(kProcessPhaseDataKey); + if (phase != process_data_snapshot.end()) + exit_phase = static_cast(phase->second.GetInt()); + + // Perform the callback. + process_exit_callback.Run(process_id, exit_stamp, exit_code, exit_phase, + std::move(command_line), + std::move(process_data_snapshot)); + } + + // Find all allocations associated with the exited process and free them. + uint32_t type; + while ((ref = iter.GetNext(&type)) != 0) { + switch (type) { + case kTypeIdActivityTracker: + case kTypeIdUserDataRecord: + case kTypeIdProcessDataRecord: + case ModuleInfoRecord::kPersistentTypeId: { + const void* memory = allocator_->GetAsArray( + ref, type, PersistentMemoryAllocator::kSizeAny); + if (!memory) + continue; + int64_t found_id; + int64_t create_stamp; + + // By convention, the OwningProcess structure is always the first + // field of the structure so there's no need to handle all the + // cases separately. + if (OwningProcess::GetOwningProcessId(memory, &found_id, + &create_stamp)) { + // Only change the type to be "free" if the process ID matches and + // the creation time is before the exit time (so PID re-use doesn't + // cause the erasure of something that is in-use). Memory is cleared + // here, rather than when it's needed, so as to limit the impact at + // that critical time. + if (found_id == process_id && create_stamp < exit_stamp) + allocator_->ChangeType(ref, ~type, type, /*clear=*/true); + } + } break; + } + } +} + +void GlobalActivityTracker::RecordLogMessage(StringPiece message) { + // Allocate at least one extra byte so the string is NUL terminated. All + // memory returned by the allocator is guaranteed to be zeroed. + PersistentMemoryAllocator::Reference ref = + allocator_->Allocate(message.size() + 1, kTypeIdGlobalLogMessage); + char* memory = allocator_->GetAsArray(ref, kTypeIdGlobalLogMessage, + message.size() + 1); + if (memory) { + memcpy(memory, message.data(), message.size()); + allocator_->MakeIterable(ref); + } +} + +void GlobalActivityTracker::RecordModuleInfo(const ModuleInfo& info) { + AutoLock lock(modules_lock_); + auto found = modules_.find(info.file); + if (found != modules_.end()) { + ModuleInfoRecord* record = found->second; + DCHECK(record); + + // Update the basic state of module information that has been already + // recorded. It is assumed that the string information (identifier, + // version, etc.) remain unchanged which means that there's no need + // to create a new record to accommodate a possibly longer length. + record->UpdateFrom(info); + return; + } + + ModuleInfoRecord* record = + ModuleInfoRecord::CreateFrom(info, allocator_.get()); + if (!record) + return; + allocator_->MakeIterable(record); + modules_.emplace(info.file, record); +} + +void GlobalActivityTracker::RecordFieldTrial(const std::string& trial_name, + StringPiece group_name) { + const std::string key = std::string("FieldTrial.") + trial_name; + process_data_.SetString(key, group_name); +} + +void GlobalActivityTracker::RecordException(const void* pc, + const void* origin, + uint32_t code) { + RecordExceptionImpl(pc, origin, code); +} + +void GlobalActivityTracker::MarkDeleted() { + allocator_->SetMemoryState(PersistentMemoryAllocator::MEMORY_DELETED); +} + +GlobalActivityTracker::GlobalActivityTracker( + std::unique_ptr allocator, + int stack_depth, + int64_t process_id) + : allocator_(std::move(allocator)), + stack_memory_size_(ThreadActivityTracker::SizeForStackDepth(stack_depth)), + process_id_(process_id == 0 ? GetCurrentProcId() : process_id), + this_thread_tracker_(&OnTLSDestroy), + thread_tracker_count_(0), + thread_tracker_allocator_(allocator_.get(), + kTypeIdActivityTracker, + kTypeIdActivityTrackerFree, + stack_memory_size_, + kCachedThreadMemories, + /*make_iterable=*/true), + user_data_allocator_(allocator_.get(), + kTypeIdUserDataRecord, + kTypeIdUserDataRecordFree, + kUserDataSize, + kCachedUserDataMemories, + /*make_iterable=*/true), + process_data_(allocator_->GetAsArray( + AllocateFrom(allocator_.get(), + kTypeIdProcessDataRecordFree, + kProcessDataSize, + kTypeIdProcessDataRecord), + kTypeIdProcessDataRecord, + kProcessDataSize), + kProcessDataSize, + process_id_) { + DCHECK_NE(0, process_id_); + + // Ensure that there is no other global object and then make this one such. + DCHECK(!g_tracker_); + subtle::Release_Store(&g_tracker_, reinterpret_cast(this)); + + // The data records must be iterable in order to be found by an analyzer. + allocator_->MakeIterable(allocator_->GetAsReference( + process_data_.GetBaseAddress(), kTypeIdProcessDataRecord)); + + // Note that this process has launched. + SetProcessPhase(PROCESS_LAUNCHED); + + // Fetch and record all activated field trials. + FieldTrial::ActiveGroups active_groups; + FieldTrialList::GetActiveFieldTrialGroups(&active_groups); + for (auto& group : active_groups) + RecordFieldTrial(group.trial_name, group.group_name); +} + +GlobalActivityTracker::~GlobalActivityTracker() { + DCHECK(Get() == nullptr || Get() == this); + DCHECK_EQ(0, thread_tracker_count_.load(std::memory_order_relaxed)); + subtle::Release_Store(&g_tracker_, 0); +} + +void GlobalActivityTracker::ReturnTrackerMemory( + ManagedActivityTracker* tracker) { + PersistentMemoryAllocator::Reference mem_reference = tracker->mem_reference_; + void* mem_base = tracker->mem_base_; + DCHECK(mem_reference); + DCHECK(mem_base); + + // Remove the destructed tracker from the set of known ones. + DCHECK_LE(1, thread_tracker_count_.load(std::memory_order_relaxed)); + thread_tracker_count_.fetch_sub(1, std::memory_order_relaxed); + + // Release this memory for re-use at a later time. + base::AutoLock autolock(thread_tracker_allocator_lock_); + thread_tracker_allocator_.ReleaseObjectReference(mem_reference); +} + +void GlobalActivityTracker::RecordExceptionImpl(const void* pc, + const void* origin, + uint32_t code) { + // Get an existing tracker for this thread. It's not possible to create + // one at this point because such would involve memory allocations and + // other potentially complex operations that can cause failures if done + // within an exception handler. In most cases various operations will + // have already created the tracker so this shouldn't generally be a + // problem. + ThreadActivityTracker* tracker = GetTrackerForCurrentThread(); + if (!tracker) + return; + + tracker->RecordExceptionActivity(pc, origin, Activity::ACT_EXCEPTION, + ActivityData::ForException(code)); +} + +// static +void GlobalActivityTracker::OnTLSDestroy(void* value) { + delete reinterpret_cast(value); +} + +ScopedActivity::ScopedActivity(const void* program_counter, + uint8_t action, + uint32_t id, + int32_t info) + : GlobalActivityTracker::ScopedThreadActivity( + program_counter, + nullptr, + static_cast(Activity::ACT_GENERIC | action), + ActivityData::ForGeneric(id, info), + /*lock_allowed=*/true), + id_(id) { + // The action must not affect the category bits of the activity type. + DCHECK_EQ(0, action & Activity::ACT_CATEGORY_MASK); +} + +void ScopedActivity::ChangeAction(uint8_t action) { + DCHECK_EQ(0, action & Activity::ACT_CATEGORY_MASK); + ChangeTypeAndData(static_cast(Activity::ACT_GENERIC | action), + kNullActivityData); +} + +void ScopedActivity::ChangeInfo(int32_t info) { + ChangeTypeAndData(Activity::ACT_NULL, ActivityData::ForGeneric(id_, info)); +} + +void ScopedActivity::ChangeActionAndInfo(uint8_t action, int32_t info) { + DCHECK_EQ(0, action & Activity::ACT_CATEGORY_MASK); + ChangeTypeAndData(static_cast(Activity::ACT_GENERIC | action), + ActivityData::ForGeneric(id_, info)); +} + +ScopedTaskRunActivity::ScopedTaskRunActivity( + const void* program_counter, + const base::PendingTask& task) + : GlobalActivityTracker::ScopedThreadActivity( + program_counter, + task.posted_from.program_counter(), + Activity::ACT_TASK_RUN, + ActivityData::ForTask(task.sequence_num), + /*lock_allowed=*/true) {} + +ScopedLockAcquireActivity::ScopedLockAcquireActivity( + const void* program_counter, + const base::internal::LockImpl* lock) + : GlobalActivityTracker::ScopedThreadActivity( + program_counter, + nullptr, + Activity::ACT_LOCK_ACQUIRE, + ActivityData::ForLock(lock), + /*lock_allowed=*/false) {} + +ScopedEventWaitActivity::ScopedEventWaitActivity( + const void* program_counter, + const base::WaitableEvent* event) + : GlobalActivityTracker::ScopedThreadActivity( + program_counter, + nullptr, + Activity::ACT_EVENT_WAIT, + ActivityData::ForEvent(event), + /*lock_allowed=*/true) {} + +ScopedThreadJoinActivity::ScopedThreadJoinActivity( + const void* program_counter, + const base::PlatformThreadHandle* thread) + : GlobalActivityTracker::ScopedThreadActivity( + program_counter, + nullptr, + Activity::ACT_THREAD_JOIN, + ActivityData::ForThread(*thread), + /*lock_allowed=*/true) {} + +#if !defined(OS_NACL) && !defined(OS_IOS) +ScopedProcessWaitActivity::ScopedProcessWaitActivity( + const void* program_counter, + const base::Process* process) + : GlobalActivityTracker::ScopedThreadActivity( + program_counter, + nullptr, + Activity::ACT_PROCESS_WAIT, + ActivityData::ForProcess(process->Pid()), + /*lock_allowed=*/true) {} +#endif + +} // namespace debug +} // namespace base diff --git a/native/zucchini/vendor/libchrome/base/debug/activity_tracker.h b/native/zucchini/vendor/libchrome/base/debug/activity_tracker.h new file mode 100644 index 0000000..5647d86 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/debug/activity_tracker.h @@ -0,0 +1,1367 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Activity tracking provides a low-overhead method of collecting information +// about the state of the application for analysis both while it is running +// and after it has terminated unexpectedly. Its primary purpose is to help +// locate reasons the browser becomes unresponsive by providing insight into +// what all the various threads and processes are (or were) doing. + +#ifndef BASE_DEBUG_ACTIVITY_TRACKER_H_ +#define BASE_DEBUG_ACTIVITY_TRACKER_H_ + +// std::atomic is undesired due to performance issues when used as global +// variables. There are no such instances here. This module uses the +// PersistentMemoryAllocator which also uses std::atomic and is written +// by the same author. +#include +#include +#include +#include +#include + +#include "base/atomicops.h" +#include "base/base_export.h" +#include "base/callback.h" +#include "base/compiler_specific.h" +#include "base/gtest_prod_util.h" +#include "base/location.h" +#include "base/memory/shared_memory.h" +#include "base/metrics/persistent_memory_allocator.h" +#include "base/process/process_handle.h" +#include "base/strings/string_piece.h" +#include "base/strings/utf_string_conversions.h" +#include "base/task_runner.h" +#include "base/threading/platform_thread.h" +#include "base/threading/thread_local_storage.h" + +namespace base { + +struct PendingTask; + +class FilePath; +class Lock; +class PlatformThreadHandle; +class Process; +class WaitableEvent; + +namespace debug { + +class ThreadActivityTracker; + + +enum : int { + // The maximum number of call-stack addresses stored per activity. This + // cannot be changed without also changing the version number of the + // structure. See kTypeIdActivityTracker in GlobalActivityTracker. + kActivityCallStackSize = 10, +}; + +// A class for keeping all information needed to verify that a structure is +// associated with a given process. +struct OwningProcess { + OwningProcess(); + ~OwningProcess(); + + // Initializes structure with the current process id and the current time. + // These can uniquely identify a process. A unique non-zero data_id will be + // set making it possible to tell using atomic reads if the data has changed. + void Release_Initialize(int64_t pid = 0); + + // Explicitly sets the process ID. + void SetOwningProcessIdForTesting(int64_t pid, int64_t stamp); + + // Gets the associated process ID, in native form, and the creation timestamp + // from memory without loading the entire structure for analysis. This will + // return false if no valid process ID is available. + static bool GetOwningProcessId(const void* memory, + int64_t* out_id, + int64_t* out_stamp); + + // SHA1(base::debug::OwningProcess): Increment this if structure changes! + static constexpr uint32_t kPersistentTypeId = 0xB1179672 + 1; + + // Expected size for 32/64-bit check by PersistentMemoryAllocator. + static constexpr size_t kExpectedInstanceSize = 24; + + std::atomic data_id; + uint32_t padding; + int64_t process_id; + int64_t create_stamp; +}; + +// The data associated with an activity is dependent upon the activity type. +// This union defines all of the various fields. All fields must be explicitly +// sized types to ensure no interoperability problems between 32-bit and +// 64-bit systems. +union ActivityData { + // Expected size for 32/64-bit check. + // TODO(bcwhite): VC2015 doesn't allow statics in unions. Fix when it does. + // static constexpr size_t kExpectedInstanceSize = 8; + + // Generic activities don't have any defined structure. + struct { + uint32_t id; // An arbitrary identifier used for association. + int32_t info; // An arbitrary value used for information purposes. + } generic; + struct { + uint64_t sequence_id; // The sequence identifier of the posted task. + } task; + struct { + uint64_t lock_address; // The memory address of the lock object. + } lock; + struct { + uint64_t event_address; // The memory address of the event object. + } event; + struct { + int64_t thread_id; // A unique identifier for a thread within a process. + } thread; + struct { + int64_t process_id; // A unique identifier for a process. + } process; + struct { + uint32_t code; // An "exception code" number. + } exception; + + // These methods create an ActivityData object from the appropriate + // parameters. Objects of this type should always be created this way to + // ensure that no fields remain unpopulated should the set of recorded + // fields change. They're defined inline where practical because they + // reduce to loading a small local structure with a few values, roughly + // the same as loading all those values into parameters. + + static ActivityData ForGeneric(uint32_t id, int32_t info) { + ActivityData data; + data.generic.id = id; + data.generic.info = info; + return data; + } + + static ActivityData ForTask(uint64_t sequence) { + ActivityData data; + data.task.sequence_id = sequence; + return data; + } + + static ActivityData ForLock(const void* lock) { + ActivityData data; + data.lock.lock_address = reinterpret_cast(lock); + return data; + } + + static ActivityData ForEvent(const void* event) { + ActivityData data; + data.event.event_address = reinterpret_cast(event); + return data; + } + + static ActivityData ForThread(const PlatformThreadHandle& handle); + static ActivityData ForThread(const int64_t id) { + ActivityData data; + data.thread.thread_id = id; + return data; + } + + static ActivityData ForProcess(const int64_t id) { + ActivityData data; + data.process.process_id = id; + return data; + } + + static ActivityData ForException(const uint32_t code) { + ActivityData data; + data.exception.code = code; + return data; + } +}; + +// A "null" activity-data that can be passed to indicate "do not change". +extern const ActivityData kNullActivityData; + + +// A helper class that is used for managing memory allocations within a +// persistent memory allocator. Instances of this class are NOT thread-safe. +// Use from a single thread or protect access with a lock. +class BASE_EXPORT ActivityTrackerMemoryAllocator { + public: + using Reference = PersistentMemoryAllocator::Reference; + + // Creates a instance for allocating objects of a fixed |object_type|, a + // corresponding |object_free| type, and the |object_size|. An internal + // cache of the last |cache_size| released references will be kept for + // quick future fetches. If |make_iterable| then allocated objects will + // be marked "iterable" in the allocator. + ActivityTrackerMemoryAllocator(PersistentMemoryAllocator* allocator, + uint32_t object_type, + uint32_t object_free_type, + size_t object_size, + size_t cache_size, + bool make_iterable); + ~ActivityTrackerMemoryAllocator(); + + // Gets a reference to an object of the configured type. This can return + // a null reference if it was not possible to allocate the memory. + Reference GetObjectReference(); + + // Returns an object to the "free" pool. + void ReleaseObjectReference(Reference ref); + + // Helper function to access an object allocated using this instance. + template + T* GetAsObject(Reference ref) { + return allocator_->GetAsObject(ref); + } + + // Similar to GetAsObject() but converts references to arrays of objects. + template + T* GetAsArray(Reference ref, size_t count) { + return allocator_->GetAsArray(ref, object_type_, count); + } + + // The current "used size" of the internal cache, visible for testing. + size_t cache_used() const { return cache_used_; } + + private: + PersistentMemoryAllocator* const allocator_; + const uint32_t object_type_; + const uint32_t object_free_type_; + const size_t object_size_; + const size_t cache_size_; + const bool make_iterable_; + + // An iterator for going through persistent memory looking for free'd objects. + PersistentMemoryAllocator::Iterator iterator_; + + // The cache of released object memories. + std::unique_ptr cache_values_; + size_t cache_used_; + + DISALLOW_COPY_AND_ASSIGN(ActivityTrackerMemoryAllocator); +}; + + +// This structure is the full contents recorded for every activity pushed +// onto the stack. The |activity_type| indicates what is actually stored in +// the |data| field. All fields must be explicitly sized types to ensure no +// interoperability problems between 32-bit and 64-bit systems. +struct Activity { + // SHA1(base::debug::Activity): Increment this if structure changes! + static constexpr uint32_t kPersistentTypeId = 0x99425159 + 1; + // Expected size for 32/64-bit check. Update this if structure changes! + static constexpr size_t kExpectedInstanceSize = + 48 + 8 * kActivityCallStackSize; + + // The type of an activity on the stack. Activities are broken into + // categories with the category ID taking the top 4 bits and the lower + // bits representing an action within that category. This combination + // makes it easy to "switch" based on the type during analysis. + enum Type : uint8_t { + // This "null" constant is used to indicate "do not change" in calls. + ACT_NULL = 0, + + // Task activities involve callbacks posted to a thread or thread-pool + // using the PostTask() method or any of its friends. + ACT_TASK = 1 << 4, + ACT_TASK_RUN = ACT_TASK, + + // Lock activities involve the acquisition of "mutex" locks. + ACT_LOCK = 2 << 4, + ACT_LOCK_ACQUIRE = ACT_LOCK, + ACT_LOCK_RELEASE, + + // Event activities involve operations on a WaitableEvent. + ACT_EVENT = 3 << 4, + ACT_EVENT_WAIT = ACT_EVENT, + ACT_EVENT_SIGNAL, + + // Thread activities involve the life management of threads. + ACT_THREAD = 4 << 4, + ACT_THREAD_START = ACT_THREAD, + ACT_THREAD_JOIN, + + // Process activities involve the life management of processes. + ACT_PROCESS = 5 << 4, + ACT_PROCESS_START = ACT_PROCESS, + ACT_PROCESS_WAIT, + + // Exception activities indicate the occurence of something unexpected. + ACT_EXCEPTION = 14 << 4, + + // Generic activities are user defined and can be anything. + ACT_GENERIC = 15 << 4, + + // These constants can be used to separate the category and action from + // a combined activity type. + ACT_CATEGORY_MASK = 0xF << 4, + ACT_ACTION_MASK = 0xF + }; + + // Internal representation of time. During collection, this is in "ticks" + // but when returned in a snapshot, it is "wall time". + int64_t time_internal; + + // The address that pushed the activity onto the stack as a raw number. + uint64_t calling_address; + + // The address that is the origin of the activity if it not obvious from + // the call stack. This is useful for things like tasks that are posted + // from a completely different thread though most activities will leave + // it null. + uint64_t origin_address; + + // Array of program-counters that make up the top of the call stack. + // Despite the fixed size, this list is always null-terminated. Entries + // after the terminator have no meaning and may or may not also be null. + // The list will be completely empty if call-stack collection is not + // enabled. + uint64_t call_stack[kActivityCallStackSize]; + + // Reference to arbitrary user data within the persistent memory segment + // and a unique identifier for it. + uint32_t user_data_ref; + uint32_t user_data_id; + + // The (enumerated) type of the activity. This defines what fields of the + // |data| record are valid. + uint8_t activity_type; + + // Padding to ensure that the next member begins on a 64-bit boundary + // even on 32-bit builds which ensures inter-operability between CPU + // architectures. New fields can be taken from this space. + uint8_t padding[7]; + + // Information specific to the |activity_type|. + ActivityData data; + + static void FillFrom(Activity* activity, + const void* program_counter, + const void* origin, + Type type, + const ActivityData& data); +}; + +// This class manages arbitrary user data that can be associated with activities +// done by a thread by supporting key/value pairs of any type. This can provide +// additional information during debugging. It is also used to store arbitrary +// global data. All updates must be done from the same thread though other +// threads can read it concurrently if they create new objects using the same +// memory. +class BASE_EXPORT ActivityUserData { + public: + // List of known value type. REFERENCE types must immediately follow the non- + // external types. + enum ValueType : uint8_t { + END_OF_VALUES = 0, + RAW_VALUE, + RAW_VALUE_REFERENCE, + STRING_VALUE, + STRING_VALUE_REFERENCE, + CHAR_VALUE, + BOOL_VALUE, + SIGNED_VALUE, + UNSIGNED_VALUE, + }; + + class BASE_EXPORT TypedValue { + public: + TypedValue(); + TypedValue(const TypedValue& other); + ~TypedValue(); + + ValueType type() const { return type_; } + + // These methods return the extracted value in the correct format. + StringPiece Get() const; + StringPiece GetString() const; + bool GetBool() const; + char GetChar() const; + int64_t GetInt() const; + uint64_t GetUint() const; + + // These methods return references to process memory as originally provided + // to corresponding Set calls. USE WITH CAUTION! There is no guarantee that + // the referenced memory is assessible or useful. It's possible that: + // - the memory was free'd and reallocated for a different purpose + // - the memory has been released back to the OS + // - the memory belongs to a different process's address space + // Dereferencing the returned StringPiece when the memory is not accessible + // will cause the program to SEGV! + StringPiece GetReference() const; + StringPiece GetStringReference() const; + + private: + friend class ActivityUserData; + + ValueType type_ = END_OF_VALUES; + uint64_t short_value_; // Used to hold copy of numbers, etc. + std::string long_value_; // Used to hold copy of raw/string data. + StringPiece ref_value_; // Used to hold reference to external data. + }; + + using Snapshot = std::map; + + // Initialize the object either as a "sink" that just accepts and discards + // data or an active one that writes to a given (zeroed) memory block. + ActivityUserData(); + ActivityUserData(void* memory, size_t size, int64_t pid = 0); + virtual ~ActivityUserData(); + + // Gets the unique ID number for this user data. If this changes then the + // contents have been overwritten by another thread. The return value is + // always non-zero unless it's actually just a data "sink". + uint32_t id() const { + return header_ ? header_->owner.data_id.load(std::memory_order_relaxed) : 0; + } + + // Writes a |value| (as part of a key/value pair) that will be included with + // the activity in any reports. The same |name| can be written multiple times + // with each successive call overwriting the previously stored |value|. For + // raw and string values, the maximum size of successive writes is limited by + // the first call. The length of "name" is limited to 255 characters. + // + // This information is stored on a "best effort" basis. It may be dropped if + // the memory buffer is full or the associated activity is beyond the maximum + // recording depth. + void Set(StringPiece name, const void* memory, size_t size) { + Set(name, RAW_VALUE, memory, size); + } + void SetString(StringPiece name, StringPiece value) { + Set(name, STRING_VALUE, value.data(), value.length()); + } + void SetString(StringPiece name, StringPiece16 value) { + SetString(name, UTF16ToUTF8(value)); + } + void SetBool(StringPiece name, bool value) { + char cvalue = value ? 1 : 0; + Set(name, BOOL_VALUE, &cvalue, sizeof(cvalue)); + } + void SetChar(StringPiece name, char value) { + Set(name, CHAR_VALUE, &value, sizeof(value)); + } + void SetInt(StringPiece name, int64_t value) { + Set(name, SIGNED_VALUE, &value, sizeof(value)); + } + void SetUint(StringPiece name, uint64_t value) { + Set(name, UNSIGNED_VALUE, &value, sizeof(value)); + } + + // These function as above but don't actually copy the data into the + // persistent memory. They store unaltered pointers along with a size. These + // can be used in conjuction with a memory dump to find certain large pieces + // of information. + void SetReference(StringPiece name, const void* memory, size_t size) { + SetReference(name, RAW_VALUE_REFERENCE, memory, size); + } + void SetStringReference(StringPiece name, StringPiece value) { + SetReference(name, STRING_VALUE_REFERENCE, value.data(), value.length()); + } + + // Creates a snapshot of the key/value pairs contained within. The returned + // data will be fixed, independent of whatever changes afterward. There is + // some protection against concurrent modification. This will return false + // if the data is invalid or if a complete overwrite of the contents is + // detected. + bool CreateSnapshot(Snapshot* output_snapshot) const; + + // Gets the base memory address used for storing data. + const void* GetBaseAddress() const; + + // Explicitly sets the process ID. + void SetOwningProcessIdForTesting(int64_t pid, int64_t stamp); + + // Gets the associated process ID, in native form, and the creation timestamp + // from tracker memory without loading the entire structure for analysis. This + // will return false if no valid process ID is available. + static bool GetOwningProcessId(const void* memory, + int64_t* out_id, + int64_t* out_stamp); + + protected: + virtual void Set(StringPiece name, + ValueType type, + const void* memory, + size_t size); + + private: + FRIEND_TEST_ALL_PREFIXES(ActivityTrackerTest, UserDataTest); + + enum : size_t { kMemoryAlignment = sizeof(uint64_t) }; + + // A structure that defines the structure header in memory. + struct MemoryHeader { + MemoryHeader(); + ~MemoryHeader(); + + OwningProcess owner; // Information about the creating process. + }; + + // Header to a key/value record held in persistent memory. + struct FieldHeader { + FieldHeader(); + ~FieldHeader(); + + std::atomic type; // Encoded ValueType + uint8_t name_size; // Length of "name" key. + std::atomic value_size; // Actual size of of the stored value. + uint16_t record_size; // Total storage of name, value, header. + }; + + // A structure used to reference data held outside of persistent memory. + struct ReferenceRecord { + uint64_t address; + uint64_t size; + }; + + // This record is used to hold known value is a map so that they can be + // found and overwritten later. + struct ValueInfo { + ValueInfo(); + ValueInfo(ValueInfo&&); + ~ValueInfo(); + + StringPiece name; // The "key" of the record. + ValueType type; // The type of the value. + void* memory; // Where the "value" is held. + std::atomic* size_ptr; // Address of the actual size of value. + size_t extent; // The total storage of the value, + }; // typically rounded up for alignment. + + void SetReference(StringPiece name, + ValueType type, + const void* memory, + size_t size); + + // Loads any data already in the memory segment. This allows for accessing + // records created previously. If this detects that the underlying data has + // gone away (cleared by another thread/process), it will invalidate all the + // data in this object and turn it into simple "sink" with no values to + // return. + void ImportExistingData() const; + + // A map of all the values within the memory block, keyed by name for quick + // updates of the values. This is "mutable" because it changes on "const" + // objects even when the actual data values can't change. + mutable std::map values_; + + // Information about the memory block in which new data can be stored. These + // are "mutable" because they change even on "const" objects that are just + // skipping already set values. + mutable char* memory_; + mutable size_t available_; + + // A pointer to the memory header for this instance. + MemoryHeader* const header_; + + // These hold values used when initially creating the object. They are + // compared against current header values to check for outside changes. + const uint32_t orig_data_id; + const int64_t orig_process_id; + const int64_t orig_create_stamp; + + DISALLOW_COPY_AND_ASSIGN(ActivityUserData); +}; + +// This class manages tracking a stack of activities for a single thread in +// a persistent manner, implementing a bounded-size stack in a fixed-size +// memory allocation. In order to support an operational mode where another +// thread is analyzing this data in real-time, atomic operations are used +// where necessary to guarantee a consistent view from the outside. +// +// This class is not generally used directly but instead managed by the +// GlobalActivityTracker instance and updated using Scoped*Activity local +// objects. +class BASE_EXPORT ThreadActivityTracker { + public: + using ActivityId = uint32_t; + + // This structure contains all the common information about the thread so + // it doesn't have to be repeated in every entry on the stack. It is defined + // and used completely within the .cc file. + struct Header; + + // This structure holds a copy of all the internal data at the moment the + // "snapshot" operation is done. It is disconnected from the live tracker + // so that continued operation of the thread will not cause changes here. + struct BASE_EXPORT Snapshot { + // Explicit constructor/destructor are needed because of complex types + // with non-trivial default constructors and destructors. + Snapshot(); + ~Snapshot(); + + // The name of the thread as set when it was created. The name may be + // truncated due to internal length limitations. + std::string thread_name; + + // The timestamp at which this process was created. + int64_t create_stamp; + + // The process and thread IDs. These values have no meaning other than + // they uniquely identify a running process and a running thread within + // that process. Thread-IDs can be re-used across different processes + // and both can be re-used after the process/thread exits. + int64_t process_id = 0; + int64_t thread_id = 0; + + // The current stack of activities that are underway for this thread. It + // is limited in its maximum size with later entries being left off. + std::vector activity_stack; + + // The current total depth of the activity stack, including those later + // entries not recorded in the |activity_stack| vector. + uint32_t activity_stack_depth = 0; + + // The last recorded "exception" activity. + Activity last_exception; + }; + + // This is the base class for having the compiler manage an activity on the + // tracker's stack. It does nothing but call methods on the passed |tracker| + // if it is not null, making it safe (and cheap) to create these objects + // even if activity tracking is not enabled. + class BASE_EXPORT ScopedActivity { + public: + ScopedActivity(ThreadActivityTracker* tracker, + const void* program_counter, + const void* origin, + Activity::Type type, + const ActivityData& data); + ~ScopedActivity(); + + // Changes some basic metadata about the activity. + void ChangeTypeAndData(Activity::Type type, const ActivityData& data); + + protected: + // The thread tracker to which this object reports. It can be null if + // activity tracking is not (yet) enabled. + ThreadActivityTracker* const tracker_; + + // An identifier that indicates a specific activity on the stack. + ActivityId activity_id_; + + private: + DISALLOW_COPY_AND_ASSIGN(ScopedActivity); + }; + + // A ThreadActivityTracker runs on top of memory that is managed externally. + // It must be large enough for the internal header and a few Activity + // blocks. See SizeForStackDepth(). + ThreadActivityTracker(void* base, size_t size); + virtual ~ThreadActivityTracker(); + + // Indicates that an activity has started from a given |origin| address in + // the code, though it can be null if the creator's address is not known. + // The |type| and |data| describe the activity. |program_counter| should be + // the result of GetProgramCounter() where push is called. Returned is an + // ID that can be used to adjust the pushed activity. + ActivityId PushActivity(const void* program_counter, + const void* origin, + Activity::Type type, + const ActivityData& data); + + // An inlined version of the above that gets the program counter where it + // is called. + ALWAYS_INLINE + ActivityId PushActivity(const void* origin, + Activity::Type type, + const ActivityData& data) { + return PushActivity(GetProgramCounter(), origin, type, data); + } + + // Changes the activity |type| and |data| of the top-most entry on the stack. + // This is useful if the information has changed and it is desireable to + // track that change without creating a new stack entry. If the type is + // ACT_NULL or the data is kNullActivityData then that value will remain + // unchanged. The type, if changed, must remain in the same category. + // Changing both is not atomic so a snapshot operation could occur between + // the update of |type| and |data| or between update of |data| fields. + void ChangeActivity(ActivityId id, + Activity::Type type, + const ActivityData& data); + + // Indicates that an activity has completed. + void PopActivity(ActivityId id); + + // Sets the user-data information for an activity. + std::unique_ptr GetUserData( + ActivityId id, + ActivityTrackerMemoryAllocator* allocator); + + // Returns if there is true use-data associated with a given ActivityId since + // it's possible than any returned object is just a sink. + bool HasUserData(ActivityId id); + + // Release the user-data information for an activity. + void ReleaseUserData(ActivityId id, + ActivityTrackerMemoryAllocator* allocator); + + // Save an exception. |origin| is the location of the exception. + void RecordExceptionActivity(const void* program_counter, + const void* origin, + Activity::Type type, + const ActivityData& data); + + // Returns whether the current data is valid or not. It is not valid if + // corruption has been detected in the header or other data structures. + bool IsValid() const; + + // Gets a copy of the tracker contents for analysis. Returns false if a + // snapshot was not possible, perhaps because the data is not valid; the + // contents of |output_snapshot| are undefined in that case. The current + // implementation does not support concurrent snapshot operations. + bool CreateSnapshot(Snapshot* output_snapshot) const; + + // Gets the base memory address used for storing data. + const void* GetBaseAddress(); + + // Access the "data version" value so tests can determine if an activity + // was pushed and popped in a single call. + uint32_t GetDataVersionForTesting(); + + // Explicitly sets the process ID. + void SetOwningProcessIdForTesting(int64_t pid, int64_t stamp); + + // Gets the associated process ID, in native form, and the creation timestamp + // from tracker memory without loading the entire structure for analysis. This + // will return false if no valid process ID is available. + static bool GetOwningProcessId(const void* memory, + int64_t* out_id, + int64_t* out_stamp); + + // Calculates the memory size required for a given stack depth, including + // the internal header structure for the stack. + static size_t SizeForStackDepth(int stack_depth); + + private: + friend class ActivityTrackerTest; + + bool CalledOnValidThread(); + + std::unique_ptr CreateUserDataForActivity( + Activity* activity, + ActivityTrackerMemoryAllocator* allocator); + + Header* const header_; // Pointer to the Header structure. + Activity* const stack_; // The stack of activities. + +#if DCHECK_IS_ON() + // The ActivityTracker is thread bound, and will be invoked across all the + // sequences that run on the thread. A ThreadChecker does not work here, as it + // asserts on running in the same sequence each time. + const PlatformThreadRef thread_id_; // The thread this instance is bound to. +#endif + const uint32_t stack_slots_; // The total number of stack slots. + + bool valid_ = false; // Tracks whether the data is valid or not. + + DISALLOW_COPY_AND_ASSIGN(ThreadActivityTracker); +}; + + +// The global tracker manages all the individual thread trackers. Memory for +// the thread trackers is taken from a PersistentMemoryAllocator which allows +// for the data to be analyzed by a parallel process or even post-mortem. +class BASE_EXPORT GlobalActivityTracker { + public: + // Type identifiers used when storing in persistent memory so they can be + // identified during extraction; the first 4 bytes of the SHA1 of the name + // is used as a unique integer. A "version number" is added to the base + // so that, if the structure of that object changes, stored older versions + // will be safely ignored. These are public so that an external process + // can recognize records of this type within an allocator. + enum : uint32_t { + kTypeIdActivityTracker = 0x5D7381AF + 4, // SHA1(ActivityTracker) v4 + kTypeIdUserDataRecord = 0x615EDDD7 + 3, // SHA1(UserDataRecord) v3 + kTypeIdGlobalLogMessage = 0x4CF434F9 + 1, // SHA1(GlobalLogMessage) v1 + kTypeIdProcessDataRecord = kTypeIdUserDataRecord + 0x100, + + kTypeIdActivityTrackerFree = ~kTypeIdActivityTracker, + kTypeIdUserDataRecordFree = ~kTypeIdUserDataRecord, + kTypeIdProcessDataRecordFree = ~kTypeIdProcessDataRecord, + }; + + // An enumeration of common process life stages. All entries are given an + // explicit number so they are known and remain constant; this allows for + // cross-version analysis either locally or on a server. + enum ProcessPhase : int { + // The phases are generic and may have meaning to the tracker. + PROCESS_PHASE_UNKNOWN = 0, + PROCESS_LAUNCHED = 1, + PROCESS_LAUNCH_FAILED = 2, + PROCESS_EXITED_CLEANLY = 10, + PROCESS_EXITED_WITH_CODE = 11, + + // Add here whatever is useful for analysis. + PROCESS_SHUTDOWN_STARTED = 100, + PROCESS_MAIN_LOOP_STARTED = 101, + }; + + // A callback made when a process exits to allow immediate analysis of its + // data. Note that the system may reuse the |process_id| so when fetching + // records it's important to ensure that what is returned was created before + // the |exit_stamp|. Movement of |process_data| information is allowed. + using ProcessExitCallback = + Callback; + + // This structure contains information about a loaded module, as shown to + // users of the tracker. + struct BASE_EXPORT ModuleInfo { + ModuleInfo(); + ModuleInfo(ModuleInfo&& rhs); + ModuleInfo(const ModuleInfo& rhs); + ~ModuleInfo(); + + ModuleInfo& operator=(ModuleInfo&& rhs); + ModuleInfo& operator=(const ModuleInfo& rhs); + + // Information about where and when the module was loaded/unloaded. + bool is_loaded = false; // Was the last operation a load or unload? + uintptr_t address = 0; // Address of the last load operation. + int64_t load_time = 0; // Time of last change; set automatically. + + // Information about the module itself. These never change no matter how + // many times a module may be loaded and unloaded. + size_t size = 0; // The size of the loaded module. + uint32_t timestamp = 0; // Opaque "timestamp" for the module. + uint32_t age = 0; // Opaque "age" for the module. + uint8_t identifier[16]; // Opaque identifier (GUID, etc.) for the module. + std::string file; // The full path to the file. (UTF-8) + std::string debug_file; // The full path to the debug file. + }; + + // This is a thin wrapper around the thread-tracker's ScopedActivity that + // allows thread-safe access to data values. It is safe to use even if + // activity tracking is not enabled. + class BASE_EXPORT ScopedThreadActivity + : public ThreadActivityTracker::ScopedActivity { + public: + ScopedThreadActivity(const void* program_counter, + const void* origin, + Activity::Type type, + const ActivityData& data, + bool lock_allowed); + ~ScopedThreadActivity(); + + // Returns an object for manipulating user data. + ActivityUserData& user_data(); + + private: + // Gets (or creates) a tracker for the current thread. If locking is not + // allowed (because a lock is being tracked which would cause recursion) + // then the attempt to create one if none found will be skipped. Once + // the tracker for this thread has been created for other reasons, locks + // will be tracked. The thread-tracker uses locks. + static ThreadActivityTracker* GetOrCreateTracker(bool lock_allowed) { + GlobalActivityTracker* global_tracker = Get(); + if (!global_tracker) + return nullptr; + + // It is not safe to use TLS once TLS has been destroyed. This can happen + // if code that runs late during thread destruction tries to use a + // base::Lock. See https://crbug.com/864589. + if (base::ThreadLocalStorage::HasBeenDestroyed()) + return nullptr; + + if (lock_allowed) + return global_tracker->GetOrCreateTrackerForCurrentThread(); + else + return global_tracker->GetTrackerForCurrentThread(); + } + + // An object that manages additional user data, created only upon request. + std::unique_ptr user_data_; + + DISALLOW_COPY_AND_ASSIGN(ScopedThreadActivity); + }; + + ~GlobalActivityTracker(); + + // Creates a global tracker using a given persistent-memory |allocator| and + // providing the given |stack_depth| to each thread tracker it manages. The + // created object is activated so tracking will begin immediately upon return. + // The |process_id| can be zero to get it from the OS but is taken for testing + // purposes. + static void CreateWithAllocator( + std::unique_ptr allocator, + int stack_depth, + int64_t process_id); + +#if !defined(OS_NACL) + // Like above but internally creates an allocator around a disk file with + // the specified |size| at the given |file_path|. Any existing file will be + // overwritten. The |id| and |name| are arbitrary and stored in the allocator + // for reference by whatever process reads it. Returns true if successful. + static bool CreateWithFile(const FilePath& file_path, + size_t size, + uint64_t id, + StringPiece name, + int stack_depth); +#endif // !defined(OS_NACL) + + // Like above but internally creates an allocator using local heap memory of + // the specified size. This is used primarily for unit tests. The |process_id| + // can be zero to get it from the OS but is taken for testing purposes. + static bool CreateWithLocalMemory(size_t size, + uint64_t id, + StringPiece name, + int stack_depth, + int64_t process_id); + + // Like above but internally creates an allocator using a shared-memory + // segment. The segment must already be mapped into the local memory space. + static bool CreateWithSharedMemory(std::unique_ptr shm, + uint64_t id, + StringPiece name, + int stack_depth); + + // Like above but takes a handle to an existing shared memory segment and + // maps it before creating the tracker. + static bool CreateWithSharedMemoryHandle(const SharedMemoryHandle& handle, + size_t size, + uint64_t id, + StringPiece name, + int stack_depth); + + // Gets the global activity-tracker or null if none exists. + static GlobalActivityTracker* Get() { + return reinterpret_cast( + subtle::Acquire_Load(&g_tracker_)); + } + + // Sets the global activity-tracker for testing purposes. + static void SetForTesting(std::unique_ptr tracker); + + // This access to the persistent allocator is only for testing; it extracts + // the global tracker completely. All tracked threads must exit before + // calling this. Tracking for the current thread will be automatically + // stopped. + static std::unique_ptr ReleaseForTesting(); + + // Convenience method for determining if a global tracker is active. + static bool IsEnabled() { return Get() != nullptr; } + + // Gets the persistent-memory-allocator in which data is stored. Callers + // can store additional records here to pass more information to the + // analysis process. + PersistentMemoryAllocator* allocator() { return allocator_.get(); } + + // Gets the thread's activity-tracker if it exists. This is inline for + // performance reasons and it uses thread-local-storage (TLS) so that there + // is no significant lookup time required to find the one for the calling + // thread. Ownership remains with the global tracker. + ThreadActivityTracker* GetTrackerForCurrentThread() { + return reinterpret_cast(this_thread_tracker_.Get()); + } + + // Gets the thread's activity-tracker or creates one if none exists. This + // is inline for performance reasons. Ownership remains with the global + // tracker. + ThreadActivityTracker* GetOrCreateTrackerForCurrentThread() { + ThreadActivityTracker* tracker = GetTrackerForCurrentThread(); + if (tracker) + return tracker; + return CreateTrackerForCurrentThread(); + } + + // Creates an activity-tracker for the current thread. + ThreadActivityTracker* CreateTrackerForCurrentThread(); + + // Releases the activity-tracker for the current thread (for testing only). + void ReleaseTrackerForCurrentThreadForTesting(); + + // Sets a task-runner that can be used for background work. + void SetBackgroundTaskRunner(const scoped_refptr& runner); + + // Sets an optional callback to be called when a process exits. + void SetProcessExitCallback(ProcessExitCallback callback); + + // Manages process lifetimes. These are called by the process that launched + // and reaped the subprocess, not the subprocess itself. If it is expensive + // to generate the parameters, Get() the global tracker and call these + // conditionally rather than using the static versions. + void RecordProcessLaunch(ProcessId process_id, + const FilePath::StringType& cmd); + void RecordProcessLaunch(ProcessId process_id, + const FilePath::StringType& exe, + const FilePath::StringType& args); + void RecordProcessExit(ProcessId process_id, int exit_code); + static void RecordProcessLaunchIfEnabled(ProcessId process_id, + const FilePath::StringType& cmd) { + GlobalActivityTracker* tracker = Get(); + if (tracker) + tracker->RecordProcessLaunch(process_id, cmd); + } + static void RecordProcessLaunchIfEnabled(ProcessId process_id, + const FilePath::StringType& exe, + const FilePath::StringType& args) { + GlobalActivityTracker* tracker = Get(); + if (tracker) + tracker->RecordProcessLaunch(process_id, exe, args); + } + static void RecordProcessExitIfEnabled(ProcessId process_id, int exit_code) { + GlobalActivityTracker* tracker = Get(); + if (tracker) + tracker->RecordProcessExit(process_id, exit_code); + } + + // Sets the "phase" of the current process, useful for knowing what it was + // doing when it last reported. + void SetProcessPhase(ProcessPhase phase); + static void SetProcessPhaseIfEnabled(ProcessPhase phase) { + GlobalActivityTracker* tracker = Get(); + if (tracker) + tracker->SetProcessPhase(phase); + } + + // Records a log message. The current implementation does NOT recycle these + // only store critical messages such as FATAL ones. + void RecordLogMessage(StringPiece message); + static void RecordLogMessageIfEnabled(StringPiece message) { + GlobalActivityTracker* tracker = Get(); + if (tracker) + tracker->RecordLogMessage(message); + } + + // Records a module load/unload event. This is safe to call multiple times + // even with the same information. + void RecordModuleInfo(const ModuleInfo& info); + static void RecordModuleInfoIfEnabled(const ModuleInfo& info) { + GlobalActivityTracker* tracker = Get(); + if (tracker) + tracker->RecordModuleInfo(info); + } + + // Record field trial information. This call is thread-safe. In addition to + // this, construction of a GlobalActivityTracker will cause all existing + // active field trials to be fetched and recorded. + void RecordFieldTrial(const std::string& trial_name, StringPiece group_name); + static void RecordFieldTrialIfEnabled(const std::string& trial_name, + StringPiece group_name) { + GlobalActivityTracker* tracker = Get(); + if (tracker) + tracker->RecordFieldTrial(trial_name, group_name); + } + + // Record exception information for the current thread. + ALWAYS_INLINE + void RecordException(const void* origin, uint32_t code) { + return RecordExceptionImpl(GetProgramCounter(), origin, code); + } + void RecordException(const void* pc, const void* origin, uint32_t code); + + // Marks the tracked data as deleted. + void MarkDeleted(); + + // Gets the process ID used for tracking. This is typically the same as what + // the OS thinks is the current process but can be overridden for testing. + int64_t process_id() { return process_id_; } + + // Accesses the process data record for storing arbitrary key/value pairs. + // Updates to this are thread-safe. + ActivityUserData& process_data() { return process_data_; } + + private: + friend class GlobalActivityAnalyzer; + friend class ScopedThreadActivity; + friend class ActivityTrackerTest; + + enum : int { + // The maximum number of threads that can be tracked within a process. If + // more than this number run concurrently, tracking of new ones may cease. + kMaxThreadCount = 100, + kCachedThreadMemories = 10, + kCachedUserDataMemories = 10, + }; + + // A wrapper around ActivityUserData that is thread-safe and thus can be used + // in the global scope without the requirement of being called from only one + // thread. + class ThreadSafeUserData : public ActivityUserData { + public: + ThreadSafeUserData(void* memory, size_t size, int64_t pid = 0); + ~ThreadSafeUserData() override; + + private: + void Set(StringPiece name, + ValueType type, + const void* memory, + size_t size) override; + + Lock data_lock_; + + DISALLOW_COPY_AND_ASSIGN(ThreadSafeUserData); + }; + + // State of a module as stored in persistent memory. This supports a single + // loading of a module only. If modules are loaded multiple times at + // different addresses, only the last will be recorded and an unload will + // not revert to the information of any other addresses. + struct BASE_EXPORT ModuleInfoRecord { + // SHA1(ModuleInfoRecord): Increment this if structure changes! + static constexpr uint32_t kPersistentTypeId = 0x05DB5F41 + 1; + + // Expected size for 32/64-bit check by PersistentMemoryAllocator. + static constexpr size_t kExpectedInstanceSize = + OwningProcess::kExpectedInstanceSize + 56; + + // The atomic unfortunately makes this a "complex" class on some compilers + // and thus requires an out-of-line constructor & destructor even though + // they do nothing. + ModuleInfoRecord(); + ~ModuleInfoRecord(); + + OwningProcess owner; // The process that created this record. + uint64_t address; // The base address of the module. + uint64_t load_time; // Time of last load/unload. + uint64_t size; // The size of the module in bytes. + uint32_t timestamp; // Opaque timestamp of the module. + uint32_t age; // Opaque "age" associated with the module. + uint8_t identifier[16]; // Opaque identifier for the module. + std::atomic changes; // Number load/unload actions. + uint16_t pickle_size; // The size of the following pickle. + uint8_t loaded; // Flag if module is loaded or not. + char pickle[1]; // Other strings; may allocate larger. + + // Decodes/encodes storage structure from more generic info structure. + bool DecodeTo(GlobalActivityTracker::ModuleInfo* info, + size_t record_size) const; + static ModuleInfoRecord* CreateFrom( + const GlobalActivityTracker::ModuleInfo& info, + PersistentMemoryAllocator* allocator); + + // Updates the core information without changing the encoded strings. This + // is useful when a known module changes state (i.e. new load or unload). + bool UpdateFrom(const GlobalActivityTracker::ModuleInfo& info); + + private: + DISALLOW_COPY_AND_ASSIGN(ModuleInfoRecord); + }; + + // A thin wrapper around the main thread-tracker that keeps additional + // information that the global tracker needs to handle joined threads. + class ManagedActivityTracker : public ThreadActivityTracker { + public: + ManagedActivityTracker(PersistentMemoryAllocator::Reference mem_reference, + void* base, + size_t size); + ~ManagedActivityTracker() override; + + // The reference into persistent memory from which the thread-tracker's + // memory was created. + const PersistentMemoryAllocator::Reference mem_reference_; + + // The physical address used for the thread-tracker's memory. + void* const mem_base_; + + private: + DISALLOW_COPY_AND_ASSIGN(ManagedActivityTracker); + }; + + // Creates a global tracker using a given persistent-memory |allocator| and + // providing the given |stack_depth| to each thread tracker it manages. The + // created object is activated so tracking has already started upon return. + // The |process_id| can be zero to get it from the OS but is taken for testing + // purposes. + GlobalActivityTracker(std::unique_ptr allocator, + int stack_depth, + int64_t process_id); + + // Returns the memory used by an activity-tracker managed by this class. + // It is called during the destruction of a ManagedActivityTracker object. + void ReturnTrackerMemory(ManagedActivityTracker* tracker); + + // Records exception information. + void RecordExceptionImpl(const void* pc, const void* origin, uint32_t code); + + // Releases the activity-tracker associcated with thread. It is called + // automatically when a thread is joined and thus there is nothing more to + // be tracked. |value| is a pointer to a ManagedActivityTracker. + static void OnTLSDestroy(void* value); + + // Does process-exit work. This can be run on any thread. + void CleanupAfterProcess(int64_t process_id, + int64_t exit_stamp, + int exit_code, + std::string&& command_line); + + // The persistent-memory allocator from which the memory for all trackers + // is taken. + std::unique_ptr allocator_; + + // The size (in bytes) of memory required by a ThreadActivityTracker to + // provide the stack-depth requested during construction. + const size_t stack_memory_size_; + + // The process-id of the current process. This is kept as a member variable, + // defined during initialization, for testing purposes. + const int64_t process_id_; + + // The activity tracker for the currently executing thread. + ThreadLocalStorage::Slot this_thread_tracker_; + + // The number of thread trackers currently active. + std::atomic thread_tracker_count_; + + // A caching memory allocator for thread-tracker objects. + ActivityTrackerMemoryAllocator thread_tracker_allocator_; + Lock thread_tracker_allocator_lock_; + + // A caching memory allocator for user data attached to activity data. + ActivityTrackerMemoryAllocator user_data_allocator_; + Lock user_data_allocator_lock_; + + // An object for holding arbitrary key value pairs with thread-safe access. + ThreadSafeUserData process_data_; + + // A map of global module information, keyed by module path. + std::map modules_; + Lock modules_lock_; + + // The active global activity tracker. + static subtle::AtomicWord g_tracker_; + + // A lock that is used to protect access to the following fields. + Lock global_tracker_lock_; + + // The collection of processes being tracked and their command-lines. + std::map known_processes_; + + // A task-runner that can be used for doing background processing. + scoped_refptr background_task_runner_; + + // A callback performed when a subprocess exits, including its exit-code + // and the phase it was in when that occurred. This will be called via + // the |background_task_runner_| if one is set or whatever thread reaped + // the process otherwise. + ProcessExitCallback process_exit_callback_; + + DISALLOW_COPY_AND_ASSIGN(GlobalActivityTracker); +}; + + +// Record entry in to and out of an arbitrary block of code. +class BASE_EXPORT ScopedActivity + : public GlobalActivityTracker::ScopedThreadActivity { + public: + // Track activity at the specified FROM_HERE location for an arbitrary + // 4-bit |action|, an arbitrary 32-bit |id|, and 32-bits of arbitrary + // |info|. None of these values affect operation; they're all purely + // for association and analysis. To have unique identifiers across a + // diverse code-base, create the number by taking the first 8 characters + // of the hash of the activity being tracked. + // + // For example: + // Tracking method: void MayNeverExit(uint32_t foo) {...} + // echo -n "MayNeverExit" | sha1sum => e44873ccab21e2b71270da24aa1... + // + // void MayNeverExit(int32_t foo) { + // base::debug::ScopedActivity track_me(0, 0xE44873CC, foo); + // ... + // } + ALWAYS_INLINE + ScopedActivity(uint8_t action, uint32_t id, int32_t info) + : ScopedActivity(GetProgramCounter(), action, id, info) {} + ScopedActivity() : ScopedActivity(0, 0, 0) {} + + // Changes the |action| and/or |info| of this activity on the stack. This + // is useful for tracking progress through a function, updating the action + // to indicate "milestones" in the block (max 16 milestones: 0-15) or the + // info to reflect other changes. Changing both is not atomic so a snapshot + // operation could occur between the update of |action| and |info|. + void ChangeAction(uint8_t action); + void ChangeInfo(int32_t info); + void ChangeActionAndInfo(uint8_t action, int32_t info); + + private: + // Constructs the object using a passed-in program-counter. + ScopedActivity(const void* program_counter, + uint8_t action, + uint32_t id, + int32_t info); + + // A copy of the ID code so it doesn't have to be passed by the caller when + // changing the |info| field. + uint32_t id_; + + DISALLOW_COPY_AND_ASSIGN(ScopedActivity); +}; + + +// These "scoped" classes provide easy tracking of various blocking actions. + +class BASE_EXPORT ScopedTaskRunActivity + : public GlobalActivityTracker::ScopedThreadActivity { + public: + ALWAYS_INLINE + explicit ScopedTaskRunActivity(const PendingTask& task) + : ScopedTaskRunActivity(GetProgramCounter(), task) {} + + private: + ScopedTaskRunActivity(const void* program_counter, const PendingTask& task); + DISALLOW_COPY_AND_ASSIGN(ScopedTaskRunActivity); +}; + +class BASE_EXPORT ScopedLockAcquireActivity + : public GlobalActivityTracker::ScopedThreadActivity { + public: + ALWAYS_INLINE + explicit ScopedLockAcquireActivity(const base::internal::LockImpl* lock) + : ScopedLockAcquireActivity(GetProgramCounter(), lock) {} + + private: + ScopedLockAcquireActivity(const void* program_counter, + const base::internal::LockImpl* lock); + DISALLOW_COPY_AND_ASSIGN(ScopedLockAcquireActivity); +}; + +class BASE_EXPORT ScopedEventWaitActivity + : public GlobalActivityTracker::ScopedThreadActivity { + public: + ALWAYS_INLINE + explicit ScopedEventWaitActivity(const WaitableEvent* event) + : ScopedEventWaitActivity(GetProgramCounter(), event) {} + + private: + ScopedEventWaitActivity(const void* program_counter, + const WaitableEvent* event); + DISALLOW_COPY_AND_ASSIGN(ScopedEventWaitActivity); +}; + +class BASE_EXPORT ScopedThreadJoinActivity + : public GlobalActivityTracker::ScopedThreadActivity { + public: + ALWAYS_INLINE + explicit ScopedThreadJoinActivity(const PlatformThreadHandle* thread) + : ScopedThreadJoinActivity(GetProgramCounter(), thread) {} + + private: + ScopedThreadJoinActivity(const void* program_counter, + const PlatformThreadHandle* thread); + DISALLOW_COPY_AND_ASSIGN(ScopedThreadJoinActivity); +}; + +// Some systems don't have base::Process +#if !defined(OS_NACL) && !defined(OS_IOS) +class BASE_EXPORT ScopedProcessWaitActivity + : public GlobalActivityTracker::ScopedThreadActivity { + public: + ALWAYS_INLINE + explicit ScopedProcessWaitActivity(const Process* process) + : ScopedProcessWaitActivity(GetProgramCounter(), process) {} + + private: + ScopedProcessWaitActivity(const void* program_counter, + const Process* process); + DISALLOW_COPY_AND_ASSIGN(ScopedProcessWaitActivity); +}; +#endif + +} // namespace debug +} // namespace base + +#endif // BASE_DEBUG_ACTIVITY_TRACKER_H_ diff --git a/native/zucchini/vendor/libchrome/base/debug/alias.cc b/native/zucchini/vendor/libchrome/base/debug/alias.cc new file mode 100644 index 0000000..ebc8b5a --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/debug/alias.cc @@ -0,0 +1,27 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/debug/alias.h" +#include "build/build_config.h" + +namespace base { +namespace debug { + +#if defined(COMPILER_MSVC) +#pragma optimize("", off) +#elif defined(__clang__) +#pragma clang optimize off +#endif + +void Alias(const void* var) { +} + +#if defined(COMPILER_MSVC) +#pragma optimize("", on) +#elif defined(__clang__) +#pragma clang optimize on +#endif + +} // namespace debug +} // namespace base diff --git a/native/zucchini/vendor/libchrome/base/debug/alias.h b/native/zucchini/vendor/libchrome/base/debug/alias.h new file mode 100644 index 0000000..128fdaa --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/debug/alias.h @@ -0,0 +1,43 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_DEBUG_ALIAS_H_ +#define BASE_DEBUG_ALIAS_H_ + +#include "base/base_export.h" +#include "base/strings/string_util.h" + +namespace base { +namespace debug { + +// Make the optimizer think that var is aliased. This is to prevent it from +// optimizing out local variables that would not otherwise be live at the point +// of a potential crash. +// base::debug::Alias should only be used for local variables, not globals, +// object members, or function return values - these must be copied to locals if +// you want to ensure they are recorded in crash dumps. +// Note that if the local variable is a pointer then its value will be retained +// but the memory that it points to will probably not be saved in the crash +// dump - by default only stack memory is saved. Therefore the aliasing +// technique is usually only worthwhile with non-pointer variables. If you have +// a pointer to an object and you want to retain the object's state you need to +// copy the object or its fields to local variables. Example usage: +// int last_error = err_; +// base::debug::Alias(&last_error); +// DEBUG_ALIAS_FOR_CSTR(name_copy, p->name, 16); +// CHECK(false); +void BASE_EXPORT Alias(const void* var); + +} // namespace debug +} // namespace base + +// Convenience macro that copies the null-terminated string from |c_str| into a +// stack-allocated char array named |var_name| that holds up to |char_count| +// characters and should be preserved in memory dumps. +#define DEBUG_ALIAS_FOR_CSTR(var_name, c_str, char_count) \ + char var_name[char_count]; \ + ::base::strlcpy(var_name, (c_str), arraysize(var_name)); \ + ::base::debug::Alias(var_name); + +#endif // BASE_DEBUG_ALIAS_H_ diff --git a/native/zucchini/vendor/libchrome/base/debug/debugger.h b/native/zucchini/vendor/libchrome/base/debug/debugger.h new file mode 100644 index 0000000..8680e28 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/debug/debugger.h @@ -0,0 +1,44 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This is a cross platform interface for helper functions related to +// debuggers. You should use this to test if you're running under a debugger, +// and if you would like to yield (breakpoint) into the debugger. + +#ifndef BASE_DEBUG_DEBUGGER_H_ +#define BASE_DEBUG_DEBUGGER_H_ + +#include "base/base_export.h" + +namespace base { +namespace debug { + +// Waits wait_seconds seconds for a debugger to attach to the current process. +// When silent is false, an exception is thrown when a debugger is detected. +BASE_EXPORT bool WaitForDebugger(int wait_seconds, bool silent); + +// Returns true if the given process is being run under a debugger. +// +// On OS X, the underlying mechanism doesn't work when the sandbox is enabled. +// To get around this, this function caches its value. +// +// WARNING: Because of this, on OS X, a call MUST be made to this function +// BEFORE the sandbox is enabled. +BASE_EXPORT bool BeingDebugged(); + +// Break into the debugger, assumes a debugger is present. +BASE_EXPORT void BreakDebugger(); + +// Used in test code, this controls whether showing dialogs and breaking into +// the debugger is suppressed for debug errors, even in debug mode (normally +// release mode doesn't do this stuff -- this is controlled separately). +// Normally UI is not suppressed. This is normally used when running automated +// tests where we want a crash rather than a dialog or a debugger. +BASE_EXPORT void SetSuppressDebugUI(bool suppress); +BASE_EXPORT bool IsDebugUISuppressed(); + +} // namespace debug +} // namespace base + +#endif // BASE_DEBUG_DEBUGGER_H_ diff --git a/native/zucchini/vendor/libchrome/base/debug/debugger_posix.cc b/native/zucchini/vendor/libchrome/base/debug/debugger_posix.cc new file mode 100644 index 0000000..b62bf01 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/debug/debugger_posix.cc @@ -0,0 +1,272 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/debug/debugger.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "base/macros.h" +#include "base/threading/platform_thread.h" +#include "base/time/time.h" +#include "build/build_config.h" + +#if defined(__GLIBCXX__) +#include +#endif + +#if defined(OS_MACOSX) +#include +#endif + +#if defined(OS_MACOSX) || defined(OS_BSD) +#include +#endif + +#if defined(OS_FREEBSD) +#include +#endif + +#include + +#include "base/debug/alias.h" +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" +#include "base/strings/string_piece.h" + +#if defined(USE_SYMBOLIZE) +#include "base/third_party/symbolize/symbolize.h" +#endif + +#if defined(OS_ANDROID) +#include "base/threading/platform_thread.h" +#endif + +namespace base { +namespace debug { + +#if defined(OS_MACOSX) || defined(OS_BSD) + +// Based on Apple's recommended method as described in +// http://developer.apple.com/qa/qa2004/qa1361.html +bool BeingDebugged() { + // NOTE: This code MUST be async-signal safe (it's used by in-process + // stack dumping signal handler). NO malloc or stdio is allowed here. + // + // While some code used below may be async-signal unsafe, note how + // the result is cached (see |is_set| and |being_debugged| static variables + // right below). If this code is properly warmed-up early + // in the start-up process, it should be safe to use later. + + // If the process is sandboxed then we can't use the sysctl, so cache the + // value. + static bool is_set = false; + static bool being_debugged = false; + + if (is_set) + return being_debugged; + + // Initialize mib, which tells sysctl what info we want. In this case, + // we're looking for information about a specific process ID. + int mib[] = { + CTL_KERN, + KERN_PROC, + KERN_PROC_PID, + getpid() +#if defined(OS_OPENBSD) + , sizeof(struct kinfo_proc), + 0 +#endif + }; + + // Caution: struct kinfo_proc is marked __APPLE_API_UNSTABLE. The source and + // binary interfaces may change. + struct kinfo_proc info; + size_t info_size = sizeof(info); + +#if defined(OS_OPENBSD) + if (sysctl(mib, arraysize(mib), NULL, &info_size, NULL, 0) < 0) + return -1; + + mib[5] = (info_size / sizeof(struct kinfo_proc)); +#endif + + int sysctl_result = sysctl(mib, arraysize(mib), &info, &info_size, NULL, 0); + DCHECK_EQ(sysctl_result, 0); + if (sysctl_result != 0) { + is_set = true; + being_debugged = false; + return being_debugged; + } + + // This process is being debugged if the P_TRACED flag is set. + is_set = true; +#if defined(OS_FREEBSD) + being_debugged = (info.ki_flag & P_TRACED) != 0; +#elif defined(OS_BSD) + being_debugged = (info.p_flag & P_TRACED) != 0; +#else + being_debugged = (info.kp_proc.p_flag & P_TRACED) != 0; +#endif + return being_debugged; +} + +#elif defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_AIX) + +// We can look in /proc/self/status for TracerPid. We are likely used in crash +// handling, so we are careful not to use the heap or have side effects. +// Another option that is common is to try to ptrace yourself, but then we +// can't detach without forking(), and that's not so great. +// static +bool BeingDebugged() { + // NOTE: This code MUST be async-signal safe (it's used by in-process + // stack dumping signal handler). NO malloc or stdio is allowed here. + + int status_fd = open("/proc/self/status", O_RDONLY); + if (status_fd == -1) + return false; + + // We assume our line will be in the first 1024 characters and that we can + // read this much all at once. In practice this will generally be true. + // This simplifies and speeds up things considerably. + char buf[1024]; + + ssize_t num_read = HANDLE_EINTR(read(status_fd, buf, sizeof(buf))); + if (IGNORE_EINTR(close(status_fd)) < 0) + return false; + + if (num_read <= 0) + return false; + + StringPiece status(buf, num_read); + StringPiece tracer("TracerPid:\t"); + + StringPiece::size_type pid_index = status.find(tracer); + if (pid_index == StringPiece::npos) + return false; + + // Our pid is 0 without a debugger, assume this for any pid starting with 0. + pid_index += tracer.size(); + return pid_index < status.size() && status[pid_index] != '0'; +} + +#elif defined(OS_FUCHSIA) + +bool BeingDebugged() { + // TODO(fuchsia): No gdb/gdbserver in the SDK yet. + return false; +} + +#else + +bool BeingDebugged() { + NOTIMPLEMENTED(); + return false; +} + +#endif + +// We want to break into the debugger in Debug mode, and cause a crash dump in +// Release mode. Breakpad behaves as follows: +// +// +-------+-----------------+-----------------+ +// | OS | Dump on SIGTRAP | Dump on SIGABRT | +// +-------+-----------------+-----------------+ +// | Linux | N | Y | +// | Mac | Y | N | +// +-------+-----------------+-----------------+ +// +// Thus we do the following: +// Linux: Debug mode if a debugger is attached, send SIGTRAP; otherwise send +// SIGABRT +// Mac: Always send SIGTRAP. + +#if defined(ARCH_CPU_ARMEL) +#define DEBUG_BREAK_ASM() asm("bkpt 0") +#elif defined(ARCH_CPU_ARM64) +#define DEBUG_BREAK_ASM() asm("brk 0") +#elif defined(ARCH_CPU_MIPS_FAMILY) +#define DEBUG_BREAK_ASM() asm("break 2") +#elif defined(ARCH_CPU_X86_FAMILY) +#define DEBUG_BREAK_ASM() asm("int3") +#endif + +#if defined(NDEBUG) && !defined(OS_MACOSX) && !defined(OS_ANDROID) +#define DEBUG_BREAK() abort() +#elif defined(OS_NACL) +// The NaCl verifier doesn't let use use int3. For now, we call abort(). We +// should ask for advice from some NaCl experts about the optimum thing here. +// http://code.google.com/p/nativeclient/issues/detail?id=645 +#define DEBUG_BREAK() abort() +#elif !defined(OS_MACOSX) +// Though Android has a "helpful" process called debuggerd to catch native +// signals on the general assumption that they are fatal errors. If no debugger +// is attached, we call abort since Breakpad needs SIGABRT to create a dump. +// When debugger is attached, for ARM platform the bkpt instruction appears +// to cause SIGBUS which is trapped by debuggerd, and we've had great +// difficulty continuing in a debugger once we stop from SIG triggered by native +// code, use GDB to set |go| to 1 to resume execution; for X86 platform, use +// "int3" to setup breakpiont and raise SIGTRAP. +// +// On other POSIX architectures, except Mac OS X, we use the same logic to +// ensure that breakpad creates a dump on crashes while it is still possible to +// use a debugger. +namespace { +void DebugBreak() { + if (!BeingDebugged()) { + abort(); + } else { +#if defined(DEBUG_BREAK_ASM) + DEBUG_BREAK_ASM(); +#else + volatile int go = 0; + while (!go) { + base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100)); + } +#endif + } +} +} // namespace +#define DEBUG_BREAK() DebugBreak() +#elif defined(DEBUG_BREAK_ASM) +#define DEBUG_BREAK() DEBUG_BREAK_ASM() +#else +#error "Don't know how to debug break on this architecture/OS" +#endif + +void BreakDebugger() { + // NOTE: This code MUST be async-signal safe (it's used by in-process + // stack dumping signal handler). NO malloc or stdio is allowed here. + + // Linker's ICF feature may merge this function with other functions with the + // same definition (e.g. any function whose sole job is to call abort()) and + // it may confuse the crash report processing system. http://crbug.com/508489 + static int static_variable_to_make_this_function_unique = 0; + base::debug::Alias(&static_variable_to_make_this_function_unique); + + DEBUG_BREAK(); +#if defined(OS_ANDROID) && !defined(OFFICIAL_BUILD) + // For Android development we always build release (debug builds are + // unmanageably large), so the unofficial build is used for debugging. It is + // helpful to be able to insert BreakDebugger() statements in the source, + // attach the debugger, inspect the state of the program and then resume it by + // setting the 'go' variable above. +#elif defined(NDEBUG) + // Terminate the program after signaling the debug break. + _exit(1); +#endif +} + +} // namespace debug +} // namespace base diff --git a/native/zucchini/vendor/libchrome/base/debug/debugging_buildflags.h b/native/zucchini/vendor/libchrome/base/debug/debugging_buildflags.h new file mode 100644 index 0000000..33e3dd0 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/debug/debugging_buildflags.h @@ -0,0 +1,12 @@ +// Generated by build/write_buildflag_header.py +// From "base_debugging_flags" +#ifndef BASE_DEBUG_DEBUGGING_FLAGS_H_ +#define BASE_DEBUG_DEBUGGING_FLAGS_H_ +#include "build/buildflag.h" +#define BUILDFLAG_INTERNAL_ENABLE_PROFILING() (0) +#define BUILDFLAG_INTERNAL_ENABLE_MEMORY_TASK_PROFILER() (0) +#define BUILDFLAG_INTERNAL_CAN_UNWIND_WITH_FRAME_POINTERS() (0) +#define BUILDFLAG_INTERNAL_ENABLE_LOCATION_SOURCE() (0) +#define BUILDFLAG_INTERNAL_CFI_ENFORCEMENT_TRAP() (0) +#define BUILDFLAG_INTERNAL_ENABLE_MUTEX_PRIORITY_INHERITANCE() (0) +#endif // BASE_DEBUG_DEBUGGING_FLAGS_H_ diff --git a/native/zucchini/vendor/libchrome/base/debug/leak_annotations.h b/native/zucchini/vendor/libchrome/base/debug/leak_annotations.h new file mode 100644 index 0000000..dc50246 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/debug/leak_annotations.h @@ -0,0 +1,46 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_DEBUG_LEAK_ANNOTATIONS_H_ +#define BASE_DEBUG_LEAK_ANNOTATIONS_H_ + +#include "base/macros.h" +#include "build/build_config.h" + +// This file defines macros which can be used to annotate intentional memory +// leaks. Support for annotations is implemented in LeakSanitizer. Annotated +// objects will be treated as a source of live pointers, i.e. any heap objects +// reachable by following pointers from an annotated object will not be +// reported as leaks. +// +// ANNOTATE_SCOPED_MEMORY_LEAK: all allocations made in the current scope +// will be annotated as leaks. +// ANNOTATE_LEAKING_OBJECT_PTR(X): the heap object referenced by pointer X will +// be annotated as a leak. + +#if defined(LEAK_SANITIZER) && !defined(OS_NACL) + +#include + +class ScopedLeakSanitizerDisabler { + public: + ScopedLeakSanitizerDisabler() { __lsan_disable(); } + ~ScopedLeakSanitizerDisabler() { __lsan_enable(); } + private: + DISALLOW_COPY_AND_ASSIGN(ScopedLeakSanitizerDisabler); +}; + +#define ANNOTATE_SCOPED_MEMORY_LEAK \ + ScopedLeakSanitizerDisabler leak_sanitizer_disabler; static_cast(0) + +#define ANNOTATE_LEAKING_OBJECT_PTR(X) __lsan_ignore_object(X); + +#else + +#define ANNOTATE_SCOPED_MEMORY_LEAK ((void)0) +#define ANNOTATE_LEAKING_OBJECT_PTR(X) ((void)0) + +#endif + +#endif // BASE_DEBUG_LEAK_ANNOTATIONS_H_ diff --git a/native/zucchini/vendor/libchrome/base/debug/proc_maps_linux.h b/native/zucchini/vendor/libchrome/base/debug/proc_maps_linux.h new file mode 100644 index 0000000..f5f8a59 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/debug/proc_maps_linux.h @@ -0,0 +1,94 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_DEBUG_PROC_MAPS_LINUX_H_ +#define BASE_DEBUG_PROC_MAPS_LINUX_H_ + +#include + +#include +#include + +#include "base/base_export.h" + +namespace base { +namespace debug { + +// Describes a region of mapped memory and the path of the file mapped. +struct MappedMemoryRegion { + enum Permission { + READ = 1 << 0, + WRITE = 1 << 1, + EXECUTE = 1 << 2, + PRIVATE = 1 << 3, // If set, region is private, otherwise it is shared. + }; + + // The address range [start,end) of mapped memory. + uintptr_t start; + uintptr_t end; + + // Byte offset into |path| of the range mapped into memory. + unsigned long long offset; + + // Image base, if this mapping corresponds to an ELF image. + uintptr_t base; + + // Bitmask of read/write/execute/private/shared permissions. + uint8_t permissions; + + // Name of the file mapped into memory. + // + // NOTE: path names aren't guaranteed to point at valid files. For example, + // "[heap]" and "[stack]" are used to represent the location of the process' + // heap and stack, respectively. + std::string path; +}; + +// Reads the data from /proc/self/maps and stores the result in |proc_maps|. +// Returns true if successful, false otherwise. +// +// There is *NO* guarantee that the resulting contents will be free of +// duplicates or even contain valid entries by time the method returns. +// +// +// THE GORY DETAILS +// +// Did you know it's next-to-impossible to atomically read the whole contents +// of /proc//maps? You would think that if we passed in a large-enough +// buffer to read() that It Should Just Work(tm), but sadly that's not the case. +// +// Linux's procfs uses seq_file [1] for handling iteration, text formatting, +// and dealing with resulting data that is larger than the size of a page. That +// last bit is especially important because it means that seq_file will never +// return more than the size of a page in a single call to read(). +// +// Unfortunately for a program like Chrome the size of /proc/self/maps is +// larger than the size of page so we're forced to call read() multiple times. +// If the virtual memory table changed in any way between calls to read() (e.g., +// a different thread calling mprotect()), it can make seq_file generate +// duplicate entries or skip entries. +// +// Even if seq_file was changed to keep flushing the contents of its page-sized +// buffer to the usermode buffer inside a single call to read(), it has to +// release its lock on the virtual memory table to handle page faults while +// copying data to usermode. This puts us in the same situation where the table +// can change while we're copying data. +// +// Alternatives such as fork()-and-suspend-the-parent-while-child-reads were +// attempted, but they present more subtle problems than it's worth. Depending +// on your use case your best bet may be to read /proc//maps prior to +// starting other threads. +// +// [1] http://kernelnewbies.org/Documents/SeqFileHowTo +BASE_EXPORT bool ReadProcMaps(std::string* proc_maps); + +// Parses /proc//maps input data and stores in |regions|. Returns true +// and updates |regions| if and only if all of |input| was successfully parsed. +BASE_EXPORT bool ParseProcMaps(const std::string& input, + std::vector* regions); + +} // namespace debug +} // namespace base + +#endif // BASE_DEBUG_PROC_MAPS_LINUX_H_ diff --git a/native/zucchini/vendor/libchrome/base/debug/stack_trace.cc b/native/zucchini/vendor/libchrome/base/debug/stack_trace.cc new file mode 100644 index 0000000..7715121 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/debug/stack_trace.cc @@ -0,0 +1,277 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/debug/stack_trace.h" + +#include + +#include +#include + +#include "base/logging.h" +#include "base/macros.h" + +#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) + +#if defined(OS_LINUX) || defined(OS_ANDROID) +#include +#include "base/process/process_handle.h" +#include "base/threading/platform_thread.h" +#endif + +#if defined(OS_MACOSX) +#include +#endif + +#if defined(OS_LINUX) && defined(__GLIBC__) +extern "C" void* __libc_stack_end; +#endif + +#endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) + +namespace base { +namespace debug { + +namespace { + +#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) + +#if defined(__arm__) && defined(__GNUC__) && !defined(__clang__) +// GCC and LLVM generate slightly different frames on ARM, see +// https://llvm.org/bugs/show_bug.cgi?id=18505 - LLVM generates +// x86-compatible frame, while GCC needs adjustment. +constexpr size_t kStackFrameAdjustment = sizeof(uintptr_t); +#else +constexpr size_t kStackFrameAdjustment = 0; +#endif + +uintptr_t GetNextStackFrame(uintptr_t fp) { + return reinterpret_cast(fp)[0] - kStackFrameAdjustment; +} + +uintptr_t GetStackFramePC(uintptr_t fp) { + return reinterpret_cast(fp)[1]; +} + +bool IsStackFrameValid(uintptr_t fp, uintptr_t prev_fp, uintptr_t stack_end) { + // With the stack growing downwards, older stack frame must be + // at a greater address that the current one. + if (fp <= prev_fp) return false; + + // Assume huge stack frames are bogus. + if (fp - prev_fp > 100000) return false; + + // Check alignment. + if (fp & (sizeof(uintptr_t) - 1)) return false; + + if (stack_end) { + // Both fp[0] and fp[1] must be within the stack. + if (fp > stack_end - 2 * sizeof(uintptr_t)) return false; + + // Additional check to filter out false positives. + if (GetStackFramePC(fp) < 32768) return false; + } + + return true; +}; + +// ScanStackForNextFrame() scans the stack for a valid frame to allow unwinding +// past system libraries. Only supported on Linux where system libraries are +// usually in the middle of the trace: +// +// TraceStackFramePointers +// +// base::WorkSourceDispatch <-- unwinding stops (next frame is invalid), +// g_main_context_dispatch ScanStackForNextFrame() is called +// +// g_main_context_iteration +// base::MessagePumpGlib::Run <-- ScanStackForNextFrame() finds valid frame, +// base::RunLoop::Run unwinding resumes +// +// __libc_start_main +// +// For stack scanning to be efficient it's very important for the thread to +// be started by Chrome. In that case we naturally terminate unwinding once +// we reach the origin of the stack (i.e. GetStackEnd()). If the thread is +// not started by Chrome (e.g. Android's main thread), then we end up always +// scanning area at the origin of the stack, wasting time and not finding any +// frames (since Android libraries don't have frame pointers). +// +// ScanStackForNextFrame() returns 0 if it couldn't find a valid frame +// (or if stack scanning is not supported on the current platform). +uintptr_t ScanStackForNextFrame(uintptr_t fp, uintptr_t stack_end) { +#if defined(OS_LINUX) + // Enough to resume almost all prematurely terminated traces. + constexpr size_t kMaxStackScanArea = 8192; + + if (!stack_end) { + // Too dangerous to scan without knowing where the stack ends. + return 0; + } + + fp += sizeof(uintptr_t); // current frame is known to be invalid + uintptr_t last_fp_to_scan = std::min(fp + kMaxStackScanArea, stack_end) - + sizeof(uintptr_t); + for (;fp <= last_fp_to_scan; fp += sizeof(uintptr_t)) { + uintptr_t next_fp = GetNextStackFrame(fp); + if (IsStackFrameValid(next_fp, fp, stack_end)) { + // Check two frames deep. Since stack frame is just a pointer to + // a higher address on the stack, it's relatively easy to find + // something that looks like one. However two linked frames are + // far less likely to be bogus. + uintptr_t next2_fp = GetNextStackFrame(next_fp); + if (IsStackFrameValid(next2_fp, next_fp, stack_end)) { + return fp; + } + } + } +#endif // defined(OS_LINUX) + + return 0; +} + +// Links stack frame |fp| to |parent_fp|, so that during stack unwinding +// TraceStackFramePointers() visits |parent_fp| after visiting |fp|. +// Both frame pointers must come from __builtin_frame_address(). +// Returns previous stack frame |fp| was linked to. +void* LinkStackFrames(void* fpp, void* parent_fp) { + uintptr_t fp = reinterpret_cast(fpp) - kStackFrameAdjustment; + void* prev_parent_fp = reinterpret_cast(fp)[0]; + reinterpret_cast(fp)[0] = parent_fp; + return prev_parent_fp; +} + +#endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) + +} // namespace + +#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) +uintptr_t GetStackEnd() { +#if defined(OS_ANDROID) + // Bionic reads proc/maps on every call to pthread_getattr_np() when called + // from the main thread. So we need to cache end of stack in that case to get + // acceptable performance. + // For all other threads pthread_getattr_np() is fast enough as it just reads + // values from its pthread_t argument. + static uintptr_t main_stack_end = 0; + + bool is_main_thread = GetCurrentProcId() == PlatformThread::CurrentId(); + if (is_main_thread && main_stack_end) { + return main_stack_end; + } + + uintptr_t stack_begin = 0; + size_t stack_size = 0; + pthread_attr_t attributes; + int error = pthread_getattr_np(pthread_self(), &attributes); + if (!error) { + error = pthread_attr_getstack( + &attributes, reinterpret_cast(&stack_begin), &stack_size); + pthread_attr_destroy(&attributes); + } + DCHECK(!error); + + uintptr_t stack_end = stack_begin + stack_size; + if (is_main_thread) { + main_stack_end = stack_end; + } + return stack_end; // 0 in case of error + +#elif defined(OS_LINUX) && defined(__GLIBC__) + + if (GetCurrentProcId() == PlatformThread::CurrentId()) { + // For the main thread we have a shortcut. + return reinterpret_cast(__libc_stack_end); + } + +// No easy way to get end of the stack for non-main threads, +// see crbug.com/617730. +#elif defined(OS_MACOSX) + return reinterpret_cast(pthread_get_stackaddr_np(pthread_self())); +#endif + + // Don't know how to get end of the stack. + return 0; +} +#endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) + +StackTrace::StackTrace() : StackTrace(arraysize(trace_)) {} + +StackTrace::StackTrace(const void* const* trace, size_t count) { + count = std::min(count, arraysize(trace_)); + if (count) + memcpy(trace_, trace, count * sizeof(trace_[0])); + count_ = count; +} + +const void *const *StackTrace::Addresses(size_t* count) const { + *count = count_; + if (count_) + return trace_; + return nullptr; +} + +std::string StackTrace::ToString() const { + std::stringstream stream; +#if !defined(__UCLIBC__) && !defined(_AIX) + OutputToStream(&stream); +#endif + return stream.str(); +} + +#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) + +size_t TraceStackFramePointers(const void** out_trace, + size_t max_depth, + size_t skip_initial) { + // Usage of __builtin_frame_address() enables frame pointers in this + // function even if they are not enabled globally. So 'fp' will always + // be valid. + uintptr_t fp = reinterpret_cast(__builtin_frame_address(0)) - + kStackFrameAdjustment; + + uintptr_t stack_end = GetStackEnd(); + + size_t depth = 0; + while (depth < max_depth) { + if (skip_initial != 0) { + skip_initial--; + } else { + out_trace[depth++] = reinterpret_cast(GetStackFramePC(fp)); + } + + uintptr_t next_fp = GetNextStackFrame(fp); + if (IsStackFrameValid(next_fp, fp, stack_end)) { + fp = next_fp; + continue; + } + + next_fp = ScanStackForNextFrame(fp, stack_end); + if (next_fp) { + fp = next_fp; + continue; + } + + // Failed to find next frame. + break; + } + + return depth; +} + +ScopedStackFrameLinker::ScopedStackFrameLinker(void* fp, void* parent_fp) + : fp_(fp), + parent_fp_(parent_fp), + original_parent_fp_(LinkStackFrames(fp, parent_fp)) {} + +ScopedStackFrameLinker::~ScopedStackFrameLinker() { + void* previous_parent_fp = LinkStackFrames(fp_, original_parent_fp_); + CHECK_EQ(parent_fp_, previous_parent_fp) + << "Stack frame's parent pointer has changed!"; +} + +#endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) + +} // namespace debug +} // namespace base diff --git a/native/zucchini/vendor/libchrome/base/debug/stack_trace.h b/native/zucchini/vendor/libchrome/base/debug/stack_trace.h new file mode 100644 index 0000000..81e6672 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/debug/stack_trace.h @@ -0,0 +1,197 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_DEBUG_STACK_TRACE_H_ +#define BASE_DEBUG_STACK_TRACE_H_ + +#include + +#include +#include + +#include "base/base_export.h" +#include "base/debug/debugging_buildflags.h" +#include "base/macros.h" +#include "build/build_config.h" + +#if defined(OS_POSIX) +#include +#endif + +#if defined(OS_WIN) +struct _EXCEPTION_POINTERS; +struct _CONTEXT; +#endif + +namespace base { +namespace debug { + +// Enables stack dump to console output on exception and signals. +// When enabled, the process will quit immediately. This is meant to be used in +// unit_tests only! This is not thread-safe: only call from main thread. +// In sandboxed processes, this has to be called before the sandbox is turned +// on. +// Calling this function on Linux opens /proc/self/maps and caches its +// contents. In non-official builds, this function also opens the object files +// that are loaded in memory and caches their file descriptors (this cannot be +// done in official builds because it has security implications). +BASE_EXPORT bool EnableInProcessStackDumping(); + +#if defined(OS_POSIX) +BASE_EXPORT void SetStackDumpFirstChanceCallback(bool (*handler)(int, + void*, + void*)); +#endif + +// Returns end of the stack, or 0 if we couldn't get it. +#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) +BASE_EXPORT uintptr_t GetStackEnd(); +#endif + +// A stacktrace can be helpful in debugging. For example, you can include a +// stacktrace member in a object (probably around #ifndef NDEBUG) so that you +// can later see where the given object was created from. +class BASE_EXPORT StackTrace { + public: + // Creates a stacktrace from the current location. + StackTrace(); + + // Creates a stacktrace from the current location, of up to |count| entries. + // |count| will be limited to at most |kMaxTraces|. + explicit StackTrace(size_t count); + + // Creates a stacktrace from an existing array of instruction + // pointers (such as returned by Addresses()). |count| will be + // limited to at most |kMaxTraces|. + StackTrace(const void* const* trace, size_t count); + +#if defined(OS_WIN) + // Creates a stacktrace for an exception. + // Note: this function will throw an import not found (StackWalk64) exception + // on system without dbghelp 5.1. + StackTrace(_EXCEPTION_POINTERS* exception_pointers); + StackTrace(const _CONTEXT* context); +#endif + + // Copying and assignment are allowed with the default functions. + + // Gets an array of instruction pointer values. |*count| will be set to the + // number of elements in the returned array. + const void* const* Addresses(size_t* count) const; + + // Prints the stack trace to stderr. + void Print() const; + +#if !defined(__UCLIBC__) & !defined(_AIX) + // Resolves backtrace to symbols and write to stream. + void OutputToStream(std::ostream* os) const; +#endif + + // Resolves backtrace to symbols and returns as string. + std::string ToString() const; + + private: +#if defined(OS_WIN) + void InitTrace(const _CONTEXT* context_record); +#endif + + // From http://msdn.microsoft.com/en-us/library/bb204633.aspx, + // the sum of FramesToSkip and FramesToCapture must be less than 63, + // so set it to 62. Even if on POSIX it could be a larger value, it usually + // doesn't give much more information. + static const int kMaxTraces = 62; + + void* trace_[kMaxTraces]; + + // The number of valid frames in |trace_|. + size_t count_; +}; + +#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) +// Traces the stack by using frame pointers. This function is faster but less +// reliable than StackTrace. It should work for debug and profiling builds, +// but not for release builds (although there are some exceptions). +// +// Writes at most |max_depth| frames (instruction pointers) into |out_trace| +// after skipping |skip_initial| frames. Note that the function itself is not +// added to the trace so |skip_initial| should be 0 in most cases. +// Returns number of frames written. +BASE_EXPORT size_t TraceStackFramePointers(const void** out_trace, + size_t max_depth, + size_t skip_initial); + +// Links stack frame |fp| to |parent_fp|, so that during stack unwinding +// TraceStackFramePointers() visits |parent_fp| after visiting |fp|. +// Both frame pointers must come from __builtin_frame_address(). +// Destructor restores original linkage of |fp| to avoid corrupting caller's +// frame register on return. +// +// This class can be used to repair broken stack frame chain in cases +// when execution flow goes into code built without frame pointers: +// +// void DoWork() { +// Call_SomeLibrary(); +// } +// static __thread void* g_saved_fp; +// void Call_SomeLibrary() { +// g_saved_fp = __builtin_frame_address(0); +// some_library_call(...); // indirectly calls SomeLibrary_Callback() +// } +// void SomeLibrary_Callback() { +// ScopedStackFrameLinker linker(__builtin_frame_address(0), g_saved_fp); +// ... +// TraceStackFramePointers(...); +// } +// +// This produces the following trace: +// +// #0 SomeLibrary_Callback() +// #1
+// #2 DoWork() +// ...rest of the trace... +// +// SomeLibrary doesn't use frame pointers, so when SomeLibrary_Callback() +// is called, stack frame register contains bogus value that becomes callback' +// parent frame address. Without ScopedStackFrameLinker unwinding would've +// stopped at that bogus frame address yielding just two first frames (#0, #1). +// ScopedStackFrameLinker overwrites callback's parent frame address with +// Call_SomeLibrary's frame, so unwinder produces full trace without even +// noticing that stack frame chain was broken. +class BASE_EXPORT ScopedStackFrameLinker { + public: + ScopedStackFrameLinker(void* fp, void* parent_fp); + ~ScopedStackFrameLinker(); + + private: + void* fp_; + void* parent_fp_; + void* original_parent_fp_; + + DISALLOW_COPY_AND_ASSIGN(ScopedStackFrameLinker); +}; + +#endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) + +namespace internal { + +#if defined(OS_POSIX) && !defined(OS_ANDROID) +// POSIX doesn't define any async-signal safe function for converting +// an integer to ASCII. We'll have to define our own version. +// itoa_r() converts a (signed) integer to ASCII. It returns "buf", if the +// conversion was successful or NULL otherwise. It never writes more than "sz" +// bytes. Output will be truncated as needed, and a NUL character is always +// appended. +BASE_EXPORT char *itoa_r(intptr_t i, + char *buf, + size_t sz, + int base, + size_t padding); +#endif // defined(OS_POSIX) && !defined(OS_ANDROID) + +} // namespace internal + +} // namespace debug +} // namespace base + +#endif // BASE_DEBUG_STACK_TRACE_H_ diff --git a/native/zucchini/vendor/libchrome/base/debug/stack_trace_posix.cc b/native/zucchini/vendor/libchrome/base/debug/stack_trace_posix.cc new file mode 100644 index 0000000..f3f05da --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/debug/stack_trace_posix.cc @@ -0,0 +1,898 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/debug/stack_trace.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#if !defined(USE_SYMBOLIZE) +#include +#endif +#if !defined(__UCLIBC__) && !defined(_AIX) +#include +#endif + +#if defined(OS_MACOSX) +#include +#endif + +#if defined(OS_LINUX) +#include "base/debug/proc_maps_linux.h" +#endif + +#include "base/cfi_buildflags.h" +#include "base/debug/debugger.h" +#include "base/files/scoped_file.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/memory/free_deleter.h" +#include "base/memory/singleton.h" +#include "base/numerics/safe_conversions.h" +#include "base/posix/eintr_wrapper.h" +#include "base/strings/string_number_conversions.h" +#include "build/build_config.h" + +#if defined(USE_SYMBOLIZE) +#include "base/third_party/symbolize/symbolize.h" +#endif + +namespace base { +namespace debug { + +namespace { + +volatile sig_atomic_t in_signal_handler = 0; + +bool (*try_handle_signal)(int, void*, void*) = nullptr; + +#if !defined(USE_SYMBOLIZE) +// The prefix used for mangled symbols, per the Itanium C++ ABI: +// http://www.codesourcery.com/cxx-abi/abi.html#mangling +const char kMangledSymbolPrefix[] = "_Z"; + +// Characters that can be used for symbols, generated by Ruby: +// (('a'..'z').to_a+('A'..'Z').to_a+('0'..'9').to_a + ['_']).join +const char kSymbolCharacters[] = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; +#endif // !defined(USE_SYMBOLIZE) + +#if !defined(USE_SYMBOLIZE) +// Demangles C++ symbols in the given text. Example: +// +// "out/Debug/base_unittests(_ZN10StackTraceC1Ev+0x20) [0x817778c]" +// => +// "out/Debug/base_unittests(StackTrace::StackTrace()+0x20) [0x817778c]" +void DemangleSymbols(std::string* text) { + // Note: code in this function is NOT async-signal safe (std::string uses + // malloc internally). + +#if !defined(__UCLIBC__) && !defined(_AIX) + std::string::size_type search_from = 0; + while (search_from < text->size()) { + // Look for the start of a mangled symbol, from search_from. + std::string::size_type mangled_start = + text->find(kMangledSymbolPrefix, search_from); + if (mangled_start == std::string::npos) { + break; // Mangled symbol not found. + } + + // Look for the end of the mangled symbol. + std::string::size_type mangled_end = + text->find_first_not_of(kSymbolCharacters, mangled_start); + if (mangled_end == std::string::npos) { + mangled_end = text->size(); + } + std::string mangled_symbol = + text->substr(mangled_start, mangled_end - mangled_start); + + // Try to demangle the mangled symbol candidate. + int status = 0; + std::unique_ptr demangled_symbol( + abi::__cxa_demangle(mangled_symbol.c_str(), nullptr, 0, &status)); + if (status == 0) { // Demangling is successful. + // Remove the mangled symbol. + text->erase(mangled_start, mangled_end - mangled_start); + // Insert the demangled symbol. + text->insert(mangled_start, demangled_symbol.get()); + // Next time, we'll start right after the demangled symbol we inserted. + search_from = mangled_start + strlen(demangled_symbol.get()); + } else { + // Failed to demangle. Retry after the "_Z" we just found. + search_from = mangled_start + 2; + } + } +#endif // !defined(__UCLIBC__) && !defined(_AIX) +} +#endif // !defined(USE_SYMBOLIZE) + +class BacktraceOutputHandler { + public: + virtual void HandleOutput(const char* output) = 0; + + protected: + virtual ~BacktraceOutputHandler() = default; +}; + +#if !defined(__UCLIBC__) && !defined(_AIX) +void OutputPointer(void* pointer, BacktraceOutputHandler* handler) { + // This should be more than enough to store a 64-bit number in hex: + // 16 hex digits + 1 for null-terminator. + char buf[17] = { '\0' }; + handler->HandleOutput("0x"); + internal::itoa_r(reinterpret_cast(pointer), + buf, sizeof(buf), 16, 12); + handler->HandleOutput(buf); +} + +#if defined(USE_SYMBOLIZE) +void OutputFrameId(intptr_t frame_id, BacktraceOutputHandler* handler) { + // Max unsigned 64-bit number in decimal has 20 digits (18446744073709551615). + // Hence, 30 digits should be more than enough to represent it in decimal + // (including the null-terminator). + char buf[30] = { '\0' }; + handler->HandleOutput("#"); + internal::itoa_r(frame_id, buf, sizeof(buf), 10, 1); + handler->HandleOutput(buf); +} +#endif // defined(USE_SYMBOLIZE) + +void ProcessBacktrace(void *const *trace, + size_t size, + BacktraceOutputHandler* handler) { + // NOTE: This code MUST be async-signal safe (it's used by in-process + // stack dumping signal handler). NO malloc or stdio is allowed here. + +#if defined(USE_SYMBOLIZE) + for (size_t i = 0; i < size; ++i) { + OutputFrameId(i, handler); + handler->HandleOutput(" "); + OutputPointer(trace[i], handler); + handler->HandleOutput(" "); + + char buf[1024] = { '\0' }; + + // Subtract by one as return address of function may be in the next + // function when a function is annotated as noreturn. + void* address = static_cast(trace[i]) - 1; + if (google::Symbolize(address, buf, sizeof(buf))) + handler->HandleOutput(buf); + else + handler->HandleOutput(""); + + handler->HandleOutput("\n"); + } +#else + bool printed = false; + + // Below part is async-signal unsafe (uses malloc), so execute it only + // when we are not executing the signal handler. + if (in_signal_handler == 0) { + std::unique_ptr trace_symbols( + backtrace_symbols(trace, size)); + if (trace_symbols.get()) { + for (size_t i = 0; i < size; ++i) { + std::string trace_symbol = trace_symbols.get()[i]; + DemangleSymbols(&trace_symbol); + handler->HandleOutput(trace_symbol.c_str()); + handler->HandleOutput("\n"); + } + + printed = true; + } + } + + if (!printed) { + for (size_t i = 0; i < size; ++i) { + handler->HandleOutput(" ["); + OutputPointer(trace[i], handler); + handler->HandleOutput("]\n"); + } + } +#endif // defined(USE_SYMBOLIZE) +} +#endif // !defined(__UCLIBC__) && !defined(_AIX) + +void PrintToStderr(const char* output) { + // NOTE: This code MUST be async-signal safe (it's used by in-process + // stack dumping signal handler). NO malloc or stdio is allowed here. + ignore_result(HANDLE_EINTR(write(STDERR_FILENO, output, strlen(output)))); +} + +void StackDumpSignalHandler(int signal, siginfo_t* info, void* void_context) { + // NOTE: This code MUST be async-signal safe. + // NO malloc or stdio is allowed here. + + // Give a registered callback a chance to recover from this signal + // + // V8 uses guard regions to guarantee memory safety in WebAssembly. This means + // some signals might be expected if they originate from Wasm code while + // accessing the guard region. We give V8 the chance to handle and recover + // from these signals first. + if (try_handle_signal != nullptr && + try_handle_signal(signal, info, void_context)) { + // The first chance handler took care of this. The SA_RESETHAND flag + // replaced this signal handler upon entry, but we want to stay + // installed. Thus, we reinstall ourselves before returning. + struct sigaction action; + memset(&action, 0, sizeof(action)); + action.sa_flags = SA_RESETHAND | SA_SIGINFO; + action.sa_sigaction = &StackDumpSignalHandler; + sigemptyset(&action.sa_mask); + + sigaction(signal, &action, nullptr); + return; + } + +// Do not take the "in signal handler" code path on Mac in a DCHECK-enabled +// build, as this prevents seeing a useful (symbolized) stack trace on a crash +// or DCHECK() failure. While it may not be fully safe to run the stack symbol +// printing code, in practice it's better to provide meaningful stack traces - +// and the risk is low given we're likely crashing already. +#if !defined(OS_MACOSX) || !DCHECK_IS_ON() + // Record the fact that we are in the signal handler now, so that the rest + // of StackTrace can behave in an async-signal-safe manner. + in_signal_handler = 1; +#endif + + if (BeingDebugged()) + BreakDebugger(); + + PrintToStderr("Received signal "); + char buf[1024] = { 0 }; + internal::itoa_r(signal, buf, sizeof(buf), 10, 0); + PrintToStderr(buf); + if (signal == SIGBUS) { + if (info->si_code == BUS_ADRALN) + PrintToStderr(" BUS_ADRALN "); + else if (info->si_code == BUS_ADRERR) + PrintToStderr(" BUS_ADRERR "); + else if (info->si_code == BUS_OBJERR) + PrintToStderr(" BUS_OBJERR "); + else + PrintToStderr(" "); + } else if (signal == SIGFPE) { + if (info->si_code == FPE_FLTDIV) + PrintToStderr(" FPE_FLTDIV "); + else if (info->si_code == FPE_FLTINV) + PrintToStderr(" FPE_FLTINV "); + else if (info->si_code == FPE_FLTOVF) + PrintToStderr(" FPE_FLTOVF "); + else if (info->si_code == FPE_FLTRES) + PrintToStderr(" FPE_FLTRES "); + else if (info->si_code == FPE_FLTSUB) + PrintToStderr(" FPE_FLTSUB "); + else if (info->si_code == FPE_FLTUND) + PrintToStderr(" FPE_FLTUND "); + else if (info->si_code == FPE_INTDIV) + PrintToStderr(" FPE_INTDIV "); + else if (info->si_code == FPE_INTOVF) + PrintToStderr(" FPE_INTOVF "); + else + PrintToStderr(" "); + } else if (signal == SIGILL) { + if (info->si_code == ILL_BADSTK) + PrintToStderr(" ILL_BADSTK "); + else if (info->si_code == ILL_COPROC) + PrintToStderr(" ILL_COPROC "); + else if (info->si_code == ILL_ILLOPN) + PrintToStderr(" ILL_ILLOPN "); + else if (info->si_code == ILL_ILLADR) + PrintToStderr(" ILL_ILLADR "); + else if (info->si_code == ILL_ILLTRP) + PrintToStderr(" ILL_ILLTRP "); + else if (info->si_code == ILL_PRVOPC) + PrintToStderr(" ILL_PRVOPC "); + else if (info->si_code == ILL_PRVREG) + PrintToStderr(" ILL_PRVREG "); + else + PrintToStderr(" "); + } else if (signal == SIGSEGV) { + if (info->si_code == SEGV_MAPERR) + PrintToStderr(" SEGV_MAPERR "); + else if (info->si_code == SEGV_ACCERR) + PrintToStderr(" SEGV_ACCERR "); + else + PrintToStderr(" "); + } + if (signal == SIGBUS || signal == SIGFPE || + signal == SIGILL || signal == SIGSEGV) { + internal::itoa_r(reinterpret_cast(info->si_addr), + buf, sizeof(buf), 16, 12); + PrintToStderr(buf); + } + PrintToStderr("\n"); + +#if BUILDFLAG(CFI_ENFORCEMENT_TRAP) + if (signal == SIGILL && info->si_code == ILL_ILLOPN) { + PrintToStderr( + "CFI: Most likely a control flow integrity violation; for more " + "information see:\n"); + PrintToStderr( + "https://www.chromium.org/developers/testing/control-flow-integrity\n"); + } +#endif // BUILDFLAG(CFI_ENFORCEMENT_TRAP) + + debug::StackTrace().Print(); + +#if defined(OS_LINUX) +#if ARCH_CPU_X86_FAMILY + ucontext_t* context = reinterpret_cast(void_context); + const struct { + const char* label; + greg_t value; + } registers[] = { +#if ARCH_CPU_32_BITS + { " gs: ", context->uc_mcontext.gregs[REG_GS] }, + { " fs: ", context->uc_mcontext.gregs[REG_FS] }, + { " es: ", context->uc_mcontext.gregs[REG_ES] }, + { " ds: ", context->uc_mcontext.gregs[REG_DS] }, + { " edi: ", context->uc_mcontext.gregs[REG_EDI] }, + { " esi: ", context->uc_mcontext.gregs[REG_ESI] }, + { " ebp: ", context->uc_mcontext.gregs[REG_EBP] }, + { " esp: ", context->uc_mcontext.gregs[REG_ESP] }, + { " ebx: ", context->uc_mcontext.gregs[REG_EBX] }, + { " edx: ", context->uc_mcontext.gregs[REG_EDX] }, + { " ecx: ", context->uc_mcontext.gregs[REG_ECX] }, + { " eax: ", context->uc_mcontext.gregs[REG_EAX] }, + { " trp: ", context->uc_mcontext.gregs[REG_TRAPNO] }, + { " err: ", context->uc_mcontext.gregs[REG_ERR] }, + { " ip: ", context->uc_mcontext.gregs[REG_EIP] }, + { " cs: ", context->uc_mcontext.gregs[REG_CS] }, + { " efl: ", context->uc_mcontext.gregs[REG_EFL] }, + { " usp: ", context->uc_mcontext.gregs[REG_UESP] }, + { " ss: ", context->uc_mcontext.gregs[REG_SS] }, +#elif ARCH_CPU_64_BITS + { " r8: ", context->uc_mcontext.gregs[REG_R8] }, + { " r9: ", context->uc_mcontext.gregs[REG_R9] }, + { " r10: ", context->uc_mcontext.gregs[REG_R10] }, + { " r11: ", context->uc_mcontext.gregs[REG_R11] }, + { " r12: ", context->uc_mcontext.gregs[REG_R12] }, + { " r13: ", context->uc_mcontext.gregs[REG_R13] }, + { " r14: ", context->uc_mcontext.gregs[REG_R14] }, + { " r15: ", context->uc_mcontext.gregs[REG_R15] }, + { " di: ", context->uc_mcontext.gregs[REG_RDI] }, + { " si: ", context->uc_mcontext.gregs[REG_RSI] }, + { " bp: ", context->uc_mcontext.gregs[REG_RBP] }, + { " bx: ", context->uc_mcontext.gregs[REG_RBX] }, + { " dx: ", context->uc_mcontext.gregs[REG_RDX] }, + { " ax: ", context->uc_mcontext.gregs[REG_RAX] }, + { " cx: ", context->uc_mcontext.gregs[REG_RCX] }, + { " sp: ", context->uc_mcontext.gregs[REG_RSP] }, + { " ip: ", context->uc_mcontext.gregs[REG_RIP] }, + { " efl: ", context->uc_mcontext.gregs[REG_EFL] }, + { " cgf: ", context->uc_mcontext.gregs[REG_CSGSFS] }, + { " erf: ", context->uc_mcontext.gregs[REG_ERR] }, + { " trp: ", context->uc_mcontext.gregs[REG_TRAPNO] }, + { " msk: ", context->uc_mcontext.gregs[REG_OLDMASK] }, + { " cr2: ", context->uc_mcontext.gregs[REG_CR2] }, +#endif // ARCH_CPU_32_BITS + }; + +#if ARCH_CPU_32_BITS + const int kRegisterPadding = 8; +#elif ARCH_CPU_64_BITS + const int kRegisterPadding = 16; +#endif + + for (size_t i = 0; i < arraysize(registers); i++) { + PrintToStderr(registers[i].label); + internal::itoa_r(registers[i].value, buf, sizeof(buf), + 16, kRegisterPadding); + PrintToStderr(buf); + + if ((i + 1) % 4 == 0) + PrintToStderr("\n"); + } + PrintToStderr("\n"); +#endif // ARCH_CPU_X86_FAMILY +#endif // defined(OS_LINUX) + + PrintToStderr("[end of stack trace]\n"); + +#if defined(OS_MACOSX) && !defined(OS_IOS) + if (::signal(signal, SIG_DFL) == SIG_ERR) + _exit(1); +#else + // Non-Mac OSes should probably reraise the signal as well, but the Linux + // sandbox tests break on CrOS devices. + // https://code.google.com/p/chromium/issues/detail?id=551681 + PrintToStderr("Calling _exit(1). Core file will not be generated.\n"); + _exit(1); +#endif // defined(OS_MACOSX) && !defined(OS_IOS) +} + +class PrintBacktraceOutputHandler : public BacktraceOutputHandler { + public: + PrintBacktraceOutputHandler() = default; + + void HandleOutput(const char* output) override { + // NOTE: This code MUST be async-signal safe (it's used by in-process + // stack dumping signal handler). NO malloc or stdio is allowed here. + PrintToStderr(output); + } + + private: + DISALLOW_COPY_AND_ASSIGN(PrintBacktraceOutputHandler); +}; + +class StreamBacktraceOutputHandler : public BacktraceOutputHandler { + public: + explicit StreamBacktraceOutputHandler(std::ostream* os) : os_(os) { + } + + void HandleOutput(const char* output) override { (*os_) << output; } + + private: + std::ostream* os_; + + DISALLOW_COPY_AND_ASSIGN(StreamBacktraceOutputHandler); +}; + +void WarmUpBacktrace() { + // Warm up stack trace infrastructure. It turns out that on the first + // call glibc initializes some internal data structures using pthread_once, + // and even backtrace() can call malloc(), leading to hangs. + // + // Example stack trace snippet (with tcmalloc): + // + // #8 0x0000000000a173b5 in tc_malloc + // at ./third_party/tcmalloc/chromium/src/debugallocation.cc:1161 + // #9 0x00007ffff7de7900 in _dl_map_object_deps at dl-deps.c:517 + // #10 0x00007ffff7ded8a9 in dl_open_worker at dl-open.c:262 + // #11 0x00007ffff7de9176 in _dl_catch_error at dl-error.c:178 + // #12 0x00007ffff7ded31a in _dl_open (file=0x7ffff625e298 "libgcc_s.so.1") + // at dl-open.c:639 + // #13 0x00007ffff6215602 in do_dlopen at dl-libc.c:89 + // #14 0x00007ffff7de9176 in _dl_catch_error at dl-error.c:178 + // #15 0x00007ffff62156c4 in dlerror_run at dl-libc.c:48 + // #16 __GI___libc_dlopen_mode at dl-libc.c:165 + // #17 0x00007ffff61ef8f5 in init + // at ../sysdeps/x86_64/../ia64/backtrace.c:53 + // #18 0x00007ffff6aad400 in pthread_once + // at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_once.S:104 + // #19 0x00007ffff61efa14 in __GI___backtrace + // at ../sysdeps/x86_64/../ia64/backtrace.c:104 + // #20 0x0000000000752a54 in base::debug::StackTrace::StackTrace + // at base/debug/stack_trace_posix.cc:175 + // #21 0x00000000007a4ae5 in + // base::(anonymous namespace)::StackDumpSignalHandler + // at base/process_util_posix.cc:172 + // #22 + StackTrace stack_trace; +} + +#if defined(USE_SYMBOLIZE) + +// class SandboxSymbolizeHelper. +// +// The purpose of this class is to prepare and install a "file open" callback +// needed by the stack trace symbolization code +// (base/third_party/symbolize/symbolize.h) so that it can function properly +// in a sandboxed process. The caveat is that this class must be instantiated +// before the sandboxing is enabled so that it can get the chance to open all +// the object files that are loaded in the virtual address space of the current +// process. +class SandboxSymbolizeHelper { + public: + // Returns the singleton instance. + static SandboxSymbolizeHelper* GetInstance() { + return Singleton>::get(); + } + + private: + friend struct DefaultSingletonTraits; + + SandboxSymbolizeHelper() + : is_initialized_(false) { + Init(); + } + + ~SandboxSymbolizeHelper() { + UnregisterCallback(); + CloseObjectFiles(); + } + + // Returns a O_RDONLY file descriptor for |file_path| if it was opened + // successfully during the initialization. The file is repositioned at + // offset 0. + // IMPORTANT: This function must be async-signal-safe because it can be + // called from a signal handler (symbolizing stack frames for a crash). + int GetFileDescriptor(const char* file_path) { + int fd = -1; + +#if !defined(OFFICIAL_BUILD) + if (file_path) { + // The assumption here is that iterating over std::map + // using a const_iterator does not allocate dynamic memory, hense it is + // async-signal-safe. + std::map::const_iterator it; + for (it = modules_.begin(); it != modules_.end(); ++it) { + if (strcmp((it->first).c_str(), file_path) == 0) { + // POSIX.1-2004 requires an implementation to guarantee that dup() + // is async-signal-safe. + fd = HANDLE_EINTR(dup(it->second)); + break; + } + } + // POSIX.1-2004 requires an implementation to guarantee that lseek() + // is async-signal-safe. + if (fd >= 0 && lseek(fd, 0, SEEK_SET) < 0) { + // Failed to seek. + fd = -1; + } + } +#endif // !defined(OFFICIAL_BUILD) + + return fd; + } + + // Searches for the object file (from /proc/self/maps) that contains + // the specified pc. If found, sets |start_address| to the start address + // of where this object file is mapped in memory, sets the module base + // address into |base_address|, copies the object file name into + // |out_file_name|, and attempts to open the object file. If the object + // file is opened successfully, returns the file descriptor. Otherwise, + // returns -1. |out_file_name_size| is the size of the file name buffer + // (including the null terminator). + // IMPORTANT: This function must be async-signal-safe because it can be + // called from a signal handler (symbolizing stack frames for a crash). + static int OpenObjectFileContainingPc(uint64_t pc, uint64_t& start_address, + uint64_t& base_address, char* file_path, + int file_path_size) { + // This method can only be called after the singleton is instantiated. + // This is ensured by the following facts: + // * This is the only static method in this class, it is private, and + // the class has no friends (except for the DefaultSingletonTraits). + // The compiler guarantees that it can only be called after the + // singleton is instantiated. + // * This method is used as a callback for the stack tracing code and + // the callback registration is done in the constructor, so logically + // it cannot be called before the singleton is created. + SandboxSymbolizeHelper* instance = GetInstance(); + + // The assumption here is that iterating over + // std::vector using a const_iterator does not allocate + // dynamic memory, hence it is async-signal-safe. + for (const MappedMemoryRegion& region : instance->regions_) { + if (region.start <= pc && pc < region.end) { + start_address = region.start; + base_address = region.base; + if (file_path && file_path_size > 0) { + strncpy(file_path, region.path.c_str(), file_path_size); + // Ensure null termination. + file_path[file_path_size - 1] = '\0'; + } + return instance->GetFileDescriptor(region.path.c_str()); + } + } + return -1; + } + + // Set the base address for each memory region by reading ELF headers in + // process memory. + void SetBaseAddressesForMemoryRegions() { + base::ScopedFD mem_fd( + HANDLE_EINTR(open("/proc/self/mem", O_RDONLY | O_CLOEXEC))); + if (!mem_fd.is_valid()) + return; + + auto safe_memcpy = [&mem_fd](void* dst, uintptr_t src, size_t size) { + return HANDLE_EINTR(pread(mem_fd.get(), dst, size, src)) == ssize_t(size); + }; + + uintptr_t cur_base = 0; + for (auto& r : regions_) { + ElfW(Ehdr) ehdr; + static_assert(SELFMAG <= sizeof(ElfW(Ehdr)), "SELFMAG too large"); + if ((r.permissions & MappedMemoryRegion::READ) && + safe_memcpy(&ehdr, r.start, sizeof(ElfW(Ehdr))) && + memcmp(ehdr.e_ident, ELFMAG, SELFMAG) == 0) { + switch (ehdr.e_type) { + case ET_EXEC: + cur_base = 0; + break; + case ET_DYN: + // Find the segment containing file offset 0. This will correspond + // to the ELF header that we just read. Normally this will have + // virtual address 0, but this is not guaranteed. We must subtract + // the virtual address from the address where the ELF header was + // mapped to get the base address. + // + // If we fail to find a segment for file offset 0, use the address + // of the ELF header as the base address. + cur_base = r.start; + for (unsigned i = 0; i != ehdr.e_phnum; ++i) { + ElfW(Phdr) phdr; + if (safe_memcpy(&phdr, r.start + ehdr.e_phoff + i * sizeof(phdr), + sizeof(phdr)) && + phdr.p_type == PT_LOAD && phdr.p_offset == 0) { + cur_base = r.start - phdr.p_vaddr; + break; + } + } + break; + default: + // ET_REL or ET_CORE. These aren't directly executable, so they + // don't affect the base address. + break; + } + } + + r.base = cur_base; + } + } + + // Parses /proc/self/maps in order to compile a list of all object file names + // for the modules that are loaded in the current process. + // Returns true on success. + bool CacheMemoryRegions() { + // Reads /proc/self/maps. + std::string contents; + if (!ReadProcMaps(&contents)) { + LOG(ERROR) << "Failed to read /proc/self/maps"; + return false; + } + + // Parses /proc/self/maps. + if (!ParseProcMaps(contents, ®ions_)) { + LOG(ERROR) << "Failed to parse the contents of /proc/self/maps"; + return false; + } + + SetBaseAddressesForMemoryRegions(); + + is_initialized_ = true; + return true; + } + + // Opens all object files and caches their file descriptors. + void OpenSymbolFiles() { + // Pre-opening and caching the file descriptors of all loaded modules is + // not safe for production builds. Hence it is only done in non-official + // builds. For more details, take a look at: http://crbug.com/341966. +#if !defined(OFFICIAL_BUILD) + // Open the object files for all read-only executable regions and cache + // their file descriptors. + std::vector::const_iterator it; + for (it = regions_.begin(); it != regions_.end(); ++it) { + const MappedMemoryRegion& region = *it; + // Only interesed in read-only executable regions. + if ((region.permissions & MappedMemoryRegion::READ) == + MappedMemoryRegion::READ && + (region.permissions & MappedMemoryRegion::WRITE) == 0 && + (region.permissions & MappedMemoryRegion::EXECUTE) == + MappedMemoryRegion::EXECUTE) { + if (region.path.empty()) { + // Skip regions with empty file names. + continue; + } + if (region.path[0] == '[') { + // Skip pseudo-paths, like [stack], [vdso], [heap], etc ... + continue; + } + // Avoid duplicates. + if (modules_.find(region.path) == modules_.end()) { + int fd = open(region.path.c_str(), O_RDONLY | O_CLOEXEC); + if (fd >= 0) { + modules_.insert(std::make_pair(region.path, fd)); + } else { + LOG(WARNING) << "Failed to open file: " << region.path + << "\n Error: " << strerror(errno); + } + } + } + } +#endif // !defined(OFFICIAL_BUILD) + } + + // Initializes and installs the symbolization callback. + void Init() { + if (CacheMemoryRegions()) { + OpenSymbolFiles(); + google::InstallSymbolizeOpenObjectFileCallback( + &OpenObjectFileContainingPc); + } + } + + // Unregister symbolization callback. + void UnregisterCallback() { + if (is_initialized_) { + google::InstallSymbolizeOpenObjectFileCallback(nullptr); + is_initialized_ = false; + } + } + + // Closes all file descriptors owned by this instance. + void CloseObjectFiles() { +#if !defined(OFFICIAL_BUILD) + std::map::iterator it; + for (it = modules_.begin(); it != modules_.end(); ++it) { + int ret = IGNORE_EINTR(close(it->second)); + DCHECK(!ret); + it->second = -1; + } + modules_.clear(); +#endif // !defined(OFFICIAL_BUILD) + } + + // Set to true upon successful initialization. + bool is_initialized_; + +#if !defined(OFFICIAL_BUILD) + // Mapping from file name to file descriptor. Includes file descriptors + // for all successfully opened object files and the file descriptor for + // /proc/self/maps. This code is not safe for production builds. + std::map modules_; +#endif // !defined(OFFICIAL_BUILD) + + // Cache for the process memory regions. Produced by parsing the contents + // of /proc/self/maps cache. + std::vector regions_; + + DISALLOW_COPY_AND_ASSIGN(SandboxSymbolizeHelper); +}; +#endif // USE_SYMBOLIZE + +} // namespace + +bool EnableInProcessStackDumping() { +#if defined(USE_SYMBOLIZE) + SandboxSymbolizeHelper::GetInstance(); +#endif // USE_SYMBOLIZE + + // When running in an application, our code typically expects SIGPIPE + // to be ignored. Therefore, when testing that same code, it should run + // with SIGPIPE ignored as well. + struct sigaction sigpipe_action; + memset(&sigpipe_action, 0, sizeof(sigpipe_action)); + sigpipe_action.sa_handler = SIG_IGN; + sigemptyset(&sigpipe_action.sa_mask); + bool success = (sigaction(SIGPIPE, &sigpipe_action, nullptr) == 0); + + // Avoid hangs during backtrace initialization, see above. + WarmUpBacktrace(); + + struct sigaction action; + memset(&action, 0, sizeof(action)); + action.sa_flags = SA_RESETHAND | SA_SIGINFO; + action.sa_sigaction = &StackDumpSignalHandler; + sigemptyset(&action.sa_mask); + + success &= (sigaction(SIGILL, &action, nullptr) == 0); + success &= (sigaction(SIGABRT, &action, nullptr) == 0); + success &= (sigaction(SIGFPE, &action, nullptr) == 0); + success &= (sigaction(SIGBUS, &action, nullptr) == 0); + success &= (sigaction(SIGSEGV, &action, nullptr) == 0); +// On Linux, SIGSYS is reserved by the kernel for seccomp-bpf sandboxing. +#if !defined(OS_LINUX) + success &= (sigaction(SIGSYS, &action, nullptr) == 0); +#endif // !defined(OS_LINUX) + + return success; +} + +void SetStackDumpFirstChanceCallback(bool (*handler)(int, void*, void*)) { + DCHECK(try_handle_signal == nullptr || handler == nullptr); + try_handle_signal = handler; +} + +StackTrace::StackTrace(size_t count) { +// NOTE: This code MUST be async-signal safe (it's used by in-process +// stack dumping signal handler). NO malloc or stdio is allowed here. + +#if !defined(__UCLIBC__) && !defined(_AIX) + count = std::min(arraysize(trace_), count); + + // Though the backtrace API man page does not list any possible negative + // return values, we take no chance. + count_ = base::saturated_cast(backtrace(trace_, count)); +#else + count_ = 0; +#endif +} + +void StackTrace::Print() const { + // NOTE: This code MUST be async-signal safe (it's used by in-process + // stack dumping signal handler). NO malloc or stdio is allowed here. + +#if !defined(__UCLIBC__) && !defined(_AIX) + PrintBacktraceOutputHandler handler; + ProcessBacktrace(trace_, count_, &handler); +#endif +} + +#if !defined(__UCLIBC__) && !defined(_AIX) +void StackTrace::OutputToStream(std::ostream* os) const { + StreamBacktraceOutputHandler handler(os); + ProcessBacktrace(trace_, count_, &handler); +} +#endif + +namespace internal { + +// NOTE: code from sandbox/linux/seccomp-bpf/demo.cc. +char* itoa_r(intptr_t i, char* buf, size_t sz, int base, size_t padding) { + // Make sure we can write at least one NUL byte. + size_t n = 1; + if (n > sz) + return nullptr; + + if (base < 2 || base > 16) { + buf[0] = '\000'; + return nullptr; + } + + char* start = buf; + + uintptr_t j = i; + + // Handle negative numbers (only for base 10). + if (i < 0 && base == 10) { + // This does "j = -i" while avoiding integer overflow. + j = static_cast(-(i + 1)) + 1; + + // Make sure we can write the '-' character. + if (++n > sz) { + buf[0] = '\000'; + return nullptr; + } + *start++ = '-'; + } + + // Loop until we have converted the entire number. Output at least one + // character (i.e. '0'). + char* ptr = start; + do { + // Make sure there is still enough space left in our output buffer. + if (++n > sz) { + buf[0] = '\000'; + return nullptr; + } + + // Output the next digit. + *ptr++ = "0123456789abcdef"[j % base]; + j /= base; + + if (padding > 0) + padding--; + } while (j > 0 || padding > 0); + + // Terminate the output with a NUL character. + *ptr = '\000'; + + // Conversion to ASCII actually resulted in the digits being in reverse + // order. We can't easily generate them in forward order, as we can't tell + // the number of characters needed until we are done converting. + // So, now, we reverse the string (except for the possible "-" sign). + while (--ptr > start) { + char ch = *ptr; + *ptr = *start; + *start++ = ch; + } + return buf; +} + +} // namespace internal + +} // namespace debug +} // namespace base diff --git a/native/zucchini/vendor/libchrome/base/environment.h b/native/zucchini/vendor/libchrome/base/environment.h new file mode 100644 index 0000000..e842ab0 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/environment.h @@ -0,0 +1,90 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_ENVIRONMENT_H_ +#define BASE_ENVIRONMENT_H_ + +#include +#include +#include + +#include "base/base_export.h" +#include "base/strings/string16.h" +#include "base/strings/string_piece.h" +#include "build/build_config.h" + +namespace base { + +namespace env_vars { + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) +BASE_EXPORT extern const char kHome[]; +#endif + +} // namespace env_vars + +class BASE_EXPORT Environment { + public: + virtual ~Environment(); + + // Returns the appropriate platform-specific instance. + static std::unique_ptr Create(); + + // Gets an environment variable's value and stores it in |result|. + // Returns false if the key is unset. + virtual bool GetVar(StringPiece variable_name, std::string* result) = 0; + + // Syntactic sugar for GetVar(variable_name, nullptr); + virtual bool HasVar(StringPiece variable_name); + + // Returns true on success, otherwise returns false. + virtual bool SetVar(StringPiece variable_name, + const std::string& new_value) = 0; + + // Returns true on success, otherwise returns false. + virtual bool UnSetVar(StringPiece variable_name) = 0; +}; + + +#if defined(OS_WIN) + +typedef string16 NativeEnvironmentString; +typedef std::map + EnvironmentMap; + +// Returns a modified environment vector constructed from the given environment +// and the list of changes given in |changes|. Each key in the environment is +// matched against the first element of the pairs. In the event of a match, the +// value is replaced by the second of the pair, unless the second is empty, in +// which case the key-value is removed. +// +// This Windows version takes and returns a Windows-style environment block +// which is a concatenated list of null-terminated 16-bit strings. The end is +// marked by a double-null terminator. The size of the returned string will +// include the terminators. +BASE_EXPORT string16 AlterEnvironment(const wchar_t* env, + const EnvironmentMap& changes); + +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + +typedef std::string NativeEnvironmentString; +typedef std::map + EnvironmentMap; + +// See general comments for the Windows version above. +// +// This Posix version takes and returns a Posix-style environment block, which +// is a null-terminated list of pointers to null-terminated strings. The +// returned array will have appended to it the storage for the array itself so +// there is only one pointer to manage, but this means that you can't copy the +// array without keeping the original around. +BASE_EXPORT std::unique_ptr AlterEnvironment( + const char* const* env, + const EnvironmentMap& changes); + +#endif + +} // namespace base + +#endif // BASE_ENVIRONMENT_H_ diff --git a/native/zucchini/vendor/libchrome/base/feature_list.h b/native/zucchini/vendor/libchrome/base/feature_list.h new file mode 100644 index 0000000..2237507 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/feature_list.h @@ -0,0 +1,310 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_FEATURE_LIST_H_ +#define BASE_FEATURE_LIST_H_ + +#include +#include +#include +#include + +#include "base/base_export.h" +#include "base/gtest_prod_util.h" +#include "base/macros.h" +#include "base/metrics/persistent_memory_allocator.h" +#include "base/strings/string_piece.h" +#include "base/synchronization/lock.h" + +namespace base { + +class FieldTrial; + +// Specifies whether a given feature is enabled or disabled by default. +enum FeatureState { + FEATURE_DISABLED_BY_DEFAULT, + FEATURE_ENABLED_BY_DEFAULT, +}; + +// The Feature struct is used to define the default state for a feature. See +// comment below for more details. There must only ever be one struct instance +// for a given feature name - generally defined as a constant global variable or +// file static. It should never be used as a constexpr as it breaks +// pointer-based identity lookup. +struct BASE_EXPORT Feature { + // The name of the feature. This should be unique to each feature and is used + // for enabling/disabling features via command line flags and experiments. + // It is strongly recommended to use CamelCase style for feature names, e.g. + // "MyGreatFeature". + const char* const name; + + // The default state (i.e. enabled or disabled) for this feature. + const FeatureState default_state; +}; + +#if DCHECK_IS_CONFIGURABLE +// DCHECKs have been built-in, and are configurable at run-time to be fatal, or +// not, via a DcheckIsFatal feature. We define the Feature here since it is +// checked in FeatureList::SetInstance(). See https://crbug.com/596231. +extern BASE_EXPORT const Feature kDCheckIsFatalFeature; +#endif // DCHECK_IS_CONFIGURABLE + +// The FeatureList class is used to determine whether a given feature is on or +// off. It provides an authoritative answer, taking into account command-line +// overrides and experimental control. +// +// The basic use case is for any feature that can be toggled (e.g. through +// command-line or an experiment) to have a defined Feature struct, e.g.: +// +// const base::Feature kMyGreatFeature { +// "MyGreatFeature", base::FEATURE_ENABLED_BY_DEFAULT +// }; +// +// Then, client code that wishes to query the state of the feature would check: +// +// if (base::FeatureList::IsEnabled(kMyGreatFeature)) { +// // Feature code goes here. +// } +// +// Behind the scenes, the above call would take into account any command-line +// flags to enable or disable the feature, any experiments that may control it +// and finally its default state (in that order of priority), to determine +// whether the feature is on. +// +// Features can be explicitly forced on or off by specifying a list of comma- +// separated feature names via the following command-line flags: +// +// --enable-features=Feature5,Feature7 +// --disable-features=Feature1,Feature2,Feature3 +// +// To enable/disable features in a test, do NOT append --enable-features or +// --disable-features to the command-line directly. Instead, use +// ScopedFeatureList. See base/test/scoped_feature_list.h for details. +// +// After initialization (which should be done single-threaded), the FeatureList +// API is thread safe. +// +// Note: This class is a singleton, but does not use base/memory/singleton.h in +// order to have control over its initialization sequence. Specifically, the +// intended use is to create an instance of this class and fully initialize it, +// before setting it as the singleton for a process, via SetInstance(). +class BASE_EXPORT FeatureList { + public: + FeatureList(); + ~FeatureList(); + + // Initializes feature overrides via command-line flags |enable_features| and + // |disable_features|, each of which is a comma-separated list of features to + // enable or disable, respectively. If a feature appears on both lists, then + // it will be disabled. If a list entry has the format "FeatureName SplitFeatureListString( + base::StringPiece input); + + // Initializes and sets an instance of FeatureList with feature overrides via + // command-line flags |enable_features| and |disable_features| if one has not + // already been set from command-line flags. Returns true if an instance did + // not previously exist. See InitializeFromCommandLine() for more details + // about |enable_features| and |disable_features| parameters. + static bool InitializeInstance(const std::string& enable_features, + const std::string& disable_features); + + // Returns the singleton instance of FeatureList. Will return null until an + // instance is registered via SetInstance(). + static FeatureList* GetInstance(); + + // Registers the given |instance| to be the singleton feature list for this + // process. This should only be called once and |instance| must not be null. + // Note: If you are considering using this for the purposes of testing, take + // a look at using base/test/scoped_feature_list.h instead. + static void SetInstance(std::unique_ptr instance); + + // Clears the previously-registered singleton instance for tests and returns + // the old instance. + // Note: Most tests should never call this directly. Instead consider using + // base::test::ScopedFeatureList. + static std::unique_ptr ClearInstanceForTesting(); + + // Sets a given (initialized) |instance| to be the singleton feature list, + // for testing. Existing instance must be null. This is primarily intended + // to support base::test::ScopedFeatureList helper class. + static void RestoreInstanceForTesting(std::unique_ptr instance); + + private: + FRIEND_TEST_ALL_PREFIXES(FeatureListTest, CheckFeatureIdentity); + FRIEND_TEST_ALL_PREFIXES(FeatureListTest, + StoreAndRetrieveFeaturesFromSharedMemory); + FRIEND_TEST_ALL_PREFIXES(FeatureListTest, + StoreAndRetrieveAssociatedFeaturesFromSharedMemory); + + struct OverrideEntry { + // The overridden enable (on/off) state of the feature. + const OverrideState overridden_state; + + // An optional associated field trial, which will be activated when the + // state of the feature is queried for the first time. Weak pointer to the + // FieldTrial object that is owned by the FieldTrialList singleton. + base::FieldTrial* field_trial; + + // Specifies whether the feature's state is overridden by |field_trial|. + // If it's not, and |field_trial| is not null, it means it is simply an + // associated field trial for reporting purposes (and |overridden_state| + // came from the command-line). + const bool overridden_by_field_trial; + + // TODO(asvitkine): Expand this as more support is added. + + // Constructs an OverrideEntry for the given |overridden_state|. If + // |field_trial| is not null, it implies that |overridden_state| comes from + // the trial, so |overridden_by_field_trial| will be set to true. + OverrideEntry(OverrideState overridden_state, FieldTrial* field_trial); + }; + + // Finalizes the initialization state of the FeatureList, so that no further + // overrides can be registered. This is called by SetInstance() on the + // singleton feature list that is being registered. + void FinalizeInitialization(); + + // Returns whether the given |feature| is enabled. This is invoked by the + // public FeatureList::IsEnabled() static function on the global singleton. + // Requires the FeatureList to have already been fully initialized. + bool IsFeatureEnabled(const Feature& feature); + + // Returns the field trial associated with the given |feature|. This is + // invoked by the public FeatureList::GetFieldTrial() static function on the + // global singleton. Requires the FeatureList to have already been fully + // initialized. + base::FieldTrial* GetAssociatedFieldTrial(const Feature& feature); + + // For each feature name in comma-separated list of strings |feature_list|, + // registers an override with the specified |overridden_state|. Also, will + // associate an optional named field trial if the entry is of the format + // "FeatureName overrides_; + + // Locked map that keeps track of seen features, to ensure a single feature is + // only defined once. This verification is only done in builds with DCHECKs + // enabled. + Lock feature_identity_tracker_lock_; + std::map feature_identity_tracker_; + + // Whether this object has been fully initialized. This gets set to true as a + // result of FinalizeInitialization(). + bool initialized_ = false; + + // Whether this object has been initialized from command line. + bool initialized_from_command_line_ = false; + + DISALLOW_COPY_AND_ASSIGN(FeatureList); +}; + +} // namespace base + +#endif // BASE_FEATURE_LIST_H_ diff --git a/native/zucchini/vendor/libchrome/base/file_descriptor_posix.h b/native/zucchini/vendor/libchrome/base/file_descriptor_posix.h new file mode 100644 index 0000000..2a36611 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/file_descriptor_posix.h @@ -0,0 +1,59 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_FILE_DESCRIPTOR_POSIX_H_ +#define BASE_FILE_DESCRIPTOR_POSIX_H_ + +#include "base/files/file.h" +#include "base/files/scoped_file.h" + +namespace base { + +// ----------------------------------------------------------------------------- +// We introduct a special structure for file descriptors in order that we are +// able to use template specialisation to special-case their handling. +// +// IMPORTANT: This is primarily intended for use when sending file descriptors +// over IPC. Even if |auto_close| is true, base::FileDescriptor does NOT close() +// |fd| when going out of scope. Instead, a consumer of a base::FileDescriptor +// must invoke close() on |fd| if |auto_close| is true. +// +// In the case of IPC, the the IPC subsystem knows to close() |fd| after sending +// a message that contains a base::FileDescriptor if auto_close == true. On the +// other end, the receiver must make sure to close() |fd| after it has finished +// processing the IPC message. See the IPC::ParamTraits<> specialization in +// ipc/ipc_message_utils.h for all the details. +// ----------------------------------------------------------------------------- +struct FileDescriptor { + FileDescriptor() : fd(-1), auto_close(false) {} + + FileDescriptor(int ifd, bool iauto_close) : fd(ifd), auto_close(iauto_close) { + } + + FileDescriptor(File file) : fd(file.TakePlatformFile()), auto_close(true) {} + explicit FileDescriptor(ScopedFD fd) : fd(fd.release()), auto_close(true) {} + + bool operator==(const FileDescriptor& other) const { + return (fd == other.fd && auto_close == other.auto_close); + } + + bool operator!=(const FileDescriptor& other) const { + return !operator==(other); + } + + // A comparison operator so that we can use these as keys in a std::map. + bool operator<(const FileDescriptor& other) const { + return other.fd < fd; + } + + int fd; + // If true, this file descriptor should be closed after it has been used. For + // example an IPC system might interpret this flag as indicating that the + // file descriptor it has been given should be closed after use. + bool auto_close; +}; + +} // namespace base + +#endif // BASE_FILE_DESCRIPTOR_POSIX_H_ diff --git a/native/zucchini/vendor/libchrome/base/files/file.h b/native/zucchini/vendor/libchrome/base/files/file.h new file mode 100644 index 0000000..30f4053 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/files/file.h @@ -0,0 +1,381 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_FILES_FILE_H_ +#define BASE_FILES_FILE_H_ + +#include + +#include + +#include "base/base_export.h" +#include "base/files/file_path.h" +#include "base/files/file_tracing.h" +#include "base/files/platform_file.h" +#include "base/files/scoped_file.h" +#include "base/macros.h" +#include "base/time/time.h" +#include "build/build_config.h" + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) +#include +#endif + +namespace base { + +#if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) || \ + defined(OS_FUCHSIA) || (defined(OS_ANDROID) && __ANDROID_API__ < 21) +typedef struct stat stat_wrapper_t; +#elif defined(OS_POSIX) +typedef struct stat64 stat_wrapper_t; +#endif + +// Thin wrapper around an OS-level file. +// Note that this class does not provide any support for asynchronous IO, other +// than the ability to create asynchronous handles on Windows. +// +// Note about const: this class does not attempt to determine if the underlying +// file system object is affected by a particular method in order to consider +// that method const or not. Only methods that deal with member variables in an +// obvious non-modifying way are marked as const. Any method that forward calls +// to the OS is not considered const, even if there is no apparent change to +// member variables. +class BASE_EXPORT File { + public: + // FLAG_(OPEN|CREATE).* are mutually exclusive. You should specify exactly one + // of the five (possibly combining with other flags) when opening or creating + // a file. + // FLAG_(WRITE|APPEND) are mutually exclusive. This is so that APPEND behavior + // will be consistent with O_APPEND on POSIX. + // FLAG_EXCLUSIVE_(READ|WRITE) only grant exclusive access to the file on + // creation on POSIX; for existing files, consider using Lock(). + enum Flags { + FLAG_OPEN = 1 << 0, // Opens a file, only if it exists. + FLAG_CREATE = 1 << 1, // Creates a new file, only if it does not + // already exist. + FLAG_OPEN_ALWAYS = 1 << 2, // May create a new file. + FLAG_CREATE_ALWAYS = 1 << 3, // May overwrite an old file. + FLAG_OPEN_TRUNCATED = 1 << 4, // Opens a file and truncates it, only if it + // exists. + FLAG_READ = 1 << 5, + FLAG_WRITE = 1 << 6, + FLAG_APPEND = 1 << 7, + FLAG_EXCLUSIVE_READ = 1 << 8, // EXCLUSIVE is opposite of Windows SHARE. + FLAG_EXCLUSIVE_WRITE = 1 << 9, + FLAG_ASYNC = 1 << 10, + FLAG_TEMPORARY = 1 << 11, // Used on Windows only. + FLAG_HIDDEN = 1 << 12, // Used on Windows only. + FLAG_DELETE_ON_CLOSE = 1 << 13, + FLAG_WRITE_ATTRIBUTES = 1 << 14, // Used on Windows only. + FLAG_SHARE_DELETE = 1 << 15, // Used on Windows only. + FLAG_TERMINAL_DEVICE = 1 << 16, // Serial port flags. + FLAG_BACKUP_SEMANTICS = 1 << 17, // Used on Windows only. + FLAG_EXECUTE = 1 << 18, // Used on Windows only. + FLAG_SEQUENTIAL_SCAN = 1 << 19, // Used on Windows only. + FLAG_CAN_DELETE_ON_CLOSE = 1 << 20, // Requests permission to delete a file + // via DeleteOnClose() (Windows only). + // See DeleteOnClose() for details. + }; + + // This enum has been recorded in multiple histograms using PlatformFileError + // enum. If the order of the fields needs to change, please ensure that those + // histograms are obsolete or have been moved to a different enum. + // + // FILE_ERROR_ACCESS_DENIED is returned when a call fails because of a + // filesystem restriction. FILE_ERROR_SECURITY is returned when a browser + // policy doesn't allow the operation to be executed. + enum Error { + FILE_OK = 0, + FILE_ERROR_FAILED = -1, + FILE_ERROR_IN_USE = -2, + FILE_ERROR_EXISTS = -3, + FILE_ERROR_NOT_FOUND = -4, + FILE_ERROR_ACCESS_DENIED = -5, + FILE_ERROR_TOO_MANY_OPENED = -6, + FILE_ERROR_NO_MEMORY = -7, + FILE_ERROR_NO_SPACE = -8, + FILE_ERROR_NOT_A_DIRECTORY = -9, + FILE_ERROR_INVALID_OPERATION = -10, + FILE_ERROR_SECURITY = -11, + FILE_ERROR_ABORT = -12, + FILE_ERROR_NOT_A_FILE = -13, + FILE_ERROR_NOT_EMPTY = -14, + FILE_ERROR_INVALID_URL = -15, + FILE_ERROR_IO = -16, + // Put new entries here and increment FILE_ERROR_MAX. + FILE_ERROR_MAX = -17 + }; + + // This explicit mapping matches both FILE_ on Windows and SEEK_ on Linux. + enum Whence { + FROM_BEGIN = 0, + FROM_CURRENT = 1, + FROM_END = 2 + }; + + // Used to hold information about a given file. + // If you add more fields to this structure (platform-specific fields are OK), + // make sure to update all functions that use it in file_util_{win|posix}.cc, + // too, and the ParamTraits implementation in + // ipc/ipc_message_utils.cc. + struct BASE_EXPORT Info { + Info(); + ~Info(); +#if defined(OS_POSIX) || defined(OS_FUCHSIA) + // Fills this struct with values from |stat_info|. + void FromStat(const stat_wrapper_t& stat_info); +#endif + + // The size of the file in bytes. Undefined when is_directory is true. + int64_t size; + + // True if the file corresponds to a directory. + bool is_directory; + + // True if the file corresponds to a symbolic link. For Windows currently + // not supported and thus always false. + bool is_symbolic_link; + + // The last modified time of a file. + Time last_modified; + + // The last accessed time of a file. + Time last_accessed; + + // The creation time of a file. + Time creation_time; + }; + + File(); + + // Creates or opens the given file. This will fail with 'access denied' if the + // |path| contains path traversal ('..') components. + File(const FilePath& path, uint32_t flags); + + // Takes ownership of |platform_file| and sets async to false. + explicit File(PlatformFile platform_file); + + // Takes ownership of |platform_file| and sets async to the given value. + // This constructor exists because on Windows you can't check if platform_file + // is async or not. + File(PlatformFile platform_file, bool async); + + // Creates an object with a specific error_details code. + explicit File(Error error_details); + + File(File&& other); + + ~File(); + + File& operator=(File&& other); + + // Creates or opens the given file. + void Initialize(const FilePath& path, uint32_t flags); + + // Returns |true| if the handle / fd wrapped by this object is valid. This + // method doesn't interact with the file system (and is safe to be called from + // ThreadRestrictions::SetIOAllowed(false) threads). + bool IsValid() const; + + // Returns true if a new file was created (or an old one truncated to zero + // length to simulate a new file, which can happen with + // FLAG_CREATE_ALWAYS), and false otherwise. + bool created() const { return created_; } + + // Returns the OS result of opening this file. Note that the way to verify + // the success of the operation is to use IsValid(), not this method: + // File file(path, flags); + // if (!file.IsValid()) + // return; + Error error_details() const { return error_details_; } + + PlatformFile GetPlatformFile() const; + PlatformFile TakePlatformFile(); + + // Destroying this object closes the file automatically. + void Close(); + + // Changes current position in the file to an |offset| relative to an origin + // defined by |whence|. Returns the resultant current position in the file + // (relative to the start) or -1 in case of error. + int64_t Seek(Whence whence, int64_t offset); + + // Reads the given number of bytes (or until EOF is reached) starting with the + // given offset. Returns the number of bytes read, or -1 on error. Note that + // this function makes a best effort to read all data on all platforms, so it + // is not intended for stream oriented files but instead for cases when the + // normal expectation is that actually |size| bytes are read unless there is + // an error. + int Read(int64_t offset, char* data, int size); + + // Same as above but without seek. + int ReadAtCurrentPos(char* data, int size); + + // Reads the given number of bytes (or until EOF is reached) starting with the + // given offset, but does not make any effort to read all data on all + // platforms. Returns the number of bytes read, or -1 on error. + int ReadNoBestEffort(int64_t offset, char* data, int size); + + // Same as above but without seek. + int ReadAtCurrentPosNoBestEffort(char* data, int size); + + // Writes the given buffer into the file at the given offset, overwritting any + // data that was previously there. Returns the number of bytes written, or -1 + // on error. Note that this function makes a best effort to write all data on + // all platforms. |data| can be nullptr when |size| is 0. + // Ignores the offset and writes to the end of the file if the file was opened + // with FLAG_APPEND. + int Write(int64_t offset, const char* data, int size); + + // Save as above but without seek. + int WriteAtCurrentPos(const char* data, int size); + + // Save as above but does not make any effort to write all data on all + // platforms. Returns the number of bytes written, or -1 on error. + int WriteAtCurrentPosNoBestEffort(const char* data, int size); + + // Returns the current size of this file, or a negative number on failure. + int64_t GetLength(); + + // Truncates the file to the given length. If |length| is greater than the + // current size of the file, the file is extended with zeros. If the file + // doesn't exist, |false| is returned. + bool SetLength(int64_t length); + + // Instructs the filesystem to flush the file to disk. (POSIX: fsync, Windows: + // FlushFileBuffers). + // Calling Flush() does not guarantee file integrity and thus is not a valid + // substitute for file integrity checks and recovery codepaths for malformed + // files. It can also be *really* slow, so avoid blocking on Flush(), + // especially please don't block shutdown on Flush(). + // Latency percentiles of Flush() across all platforms as of July 2016: + // 50 % > 5 ms + // 10 % > 58 ms + // 1 % > 357 ms + // 0.1 % > 1.8 seconds + // 0.01 % > 7.6 seconds + bool Flush(); + + // Updates the file times. + bool SetTimes(Time last_access_time, Time last_modified_time); + + // Returns some basic information for the given file. + bool GetInfo(Info* info); + +#if !defined(OS_FUCHSIA) // Fuchsia's POSIX API does not support file locking. + + // Attempts to take an exclusive write lock on the file. Returns immediately + // (i.e. does not wait for another process to unlock the file). If the lock + // was obtained, the result will be FILE_OK. A lock only guarantees + // that other processes may not also take a lock on the same file with the + // same API - it may still be opened, renamed, unlinked, etc. + // + // Common semantics: + // * Locks are held by processes, but not inherited by child processes. + // * Locks are released by the OS on file close or process termination. + // * Locks are reliable only on local filesystems. + // * Duplicated file handles may also write to locked files. + // Windows-specific semantics: + // * Locks are mandatory for read/write APIs, advisory for mapping APIs. + // * Within a process, locking the same file (by the same or new handle) + // will fail. + // POSIX-specific semantics: + // * Locks are advisory only. + // * Within a process, locking the same file (by the same or new handle) + // will succeed. + // * Closing any descriptor on a given file releases the lock. + Error Lock(); + + // Unlock a file previously locked. + Error Unlock(); + +#endif // !defined(OS_FUCHSIA) + + // Returns a new object referencing this file for use within the current + // process. Handling of FLAG_DELETE_ON_CLOSE varies by OS. On POSIX, the File + // object that was created or initialized with this flag will have unlinked + // the underlying file when it was created or opened. On Windows, the + // underlying file is deleted when the last handle to it is closed. + File Duplicate() const; + + bool async() const { return async_; } + +#if defined(OS_WIN) + // Sets or clears the DeleteFile disposition on the file. Returns true if + // the disposition was set or cleared, as indicated by |delete_on_close|. + // + // Microsoft Windows deletes a file only when the DeleteFile disposition is + // set on a file when the last handle to the last underlying kernel File + // object is closed. This disposition is be set by: + // - Calling the Win32 DeleteFile function with the path to a file. + // - Opening/creating a file with FLAG_DELETE_ON_CLOSE and then closing all + // handles to that File object. + // - Opening/creating a file with FLAG_CAN_DELETE_ON_CLOSE and subsequently + // calling DeleteOnClose(true). + // + // In all cases, all pre-existing handles to the file must have been opened + // with FLAG_SHARE_DELETE. Once the disposition has been set by any of the + // above means, no new File objects can be created for the file. + // + // So: + // - Use FLAG_SHARE_DELETE when creating/opening a file to allow another + // entity on the system to cause it to be deleted when it is closed. (Note: + // another entity can delete the file the moment after it is closed, so not + // using this permission doesn't provide any protections.) + // - Use FLAG_DELETE_ON_CLOSE for any file that is to be deleted after use. + // The OS will ensure it is deleted even in the face of process termination. + // Note that it's possible for deletion to be cancelled via another File + // object referencing the same file using DeleteOnClose(false) to clear the + // DeleteFile disposition after the original File is closed. + // - Use FLAG_CAN_DELETE_ON_CLOSE in conjunction with DeleteOnClose() to alter + // the DeleteFile disposition on an open handle. This fine-grained control + // allows for marking a file for deletion during processing so that it is + // deleted in the event of untimely process termination, and then clearing + // this state once the file is suitable for persistence. + bool DeleteOnClose(bool delete_on_close); +#endif + +#if defined(OS_WIN) + static Error OSErrorToFileError(DWORD last_error); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + static Error OSErrorToFileError(int saved_errno); +#endif + + // Gets the last global error (errno or GetLastError()) and converts it to the + // closest base::File::Error equivalent via OSErrorToFileError(). The returned + // value is only trustworthy immediately after another base::File method + // fails. base::File never resets the global error to zero. + static Error GetLastFileError(); + + // Converts an error value to a human-readable form. Used for logging. + static std::string ErrorToString(Error error); + + private: + friend class FileTracing::ScopedTrace; + + // Creates or opens the given file. Only called if |path| has no + // traversal ('..') components. + void DoInitialize(const FilePath& path, uint32_t flags); + + void SetPlatformFile(PlatformFile file); + + ScopedPlatformFile file_; + + // A path to use for tracing purposes. Set if file tracing is enabled during + // |Initialize()|. + FilePath tracing_path_; + + // Object tied to the lifetime of |this| that enables/disables tracing. + FileTracing::ScopedEnabler trace_enabler_; + + Error error_details_; + bool created_; + bool async_; + + DISALLOW_COPY_AND_ASSIGN(File); +}; + +} // namespace base + +#endif // BASE_FILES_FILE_H_ diff --git a/native/zucchini/vendor/libchrome/base/files/file_path.h b/native/zucchini/vendor/libchrome/base/files/file_path.h new file mode 100644 index 0000000..2dc15f9 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/files/file_path.h @@ -0,0 +1,481 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// FilePath is a container for pathnames stored in a platform's native string +// type, providing containers for manipulation in according with the +// platform's conventions for pathnames. It supports the following path +// types: +// +// POSIX Windows +// --------------- ---------------------------------- +// Fundamental type char[] wchar_t[] +// Encoding unspecified* UTF-16 +// Separator / \, tolerant of / +// Drive letters no case-insensitive A-Z followed by : +// Alternate root // (surprise!) \\, for UNC paths +// +// * The encoding need not be specified on POSIX systems, although some +// POSIX-compliant systems do specify an encoding. Mac OS X uses UTF-8. +// Chrome OS also uses UTF-8. +// Linux does not specify an encoding, but in practice, the locale's +// character set may be used. +// +// For more arcane bits of path trivia, see below. +// +// FilePath objects are intended to be used anywhere paths are. An +// application may pass FilePath objects around internally, masking the +// underlying differences between systems, only differing in implementation +// where interfacing directly with the system. For example, a single +// OpenFile(const FilePath &) function may be made available, allowing all +// callers to operate without regard to the underlying implementation. On +// POSIX-like platforms, OpenFile might wrap fopen, and on Windows, it might +// wrap _wfopen_s, perhaps both by calling file_path.value().c_str(). This +// allows each platform to pass pathnames around without requiring conversions +// between encodings, which has an impact on performance, but more imporantly, +// has an impact on correctness on platforms that do not have well-defined +// encodings for pathnames. +// +// Several methods are available to perform common operations on a FilePath +// object, such as determining the parent directory (DirName), isolating the +// final path component (BaseName), and appending a relative pathname string +// to an existing FilePath object (Append). These methods are highly +// recommended over attempting to split and concatenate strings directly. +// These methods are based purely on string manipulation and knowledge of +// platform-specific pathname conventions, and do not consult the filesystem +// at all, making them safe to use without fear of blocking on I/O operations. +// These methods do not function as mutators but instead return distinct +// instances of FilePath objects, and are therefore safe to use on const +// objects. The objects themselves are safe to share between threads. +// +// To aid in initialization of FilePath objects from string literals, a +// FILE_PATH_LITERAL macro is provided, which accounts for the difference +// between char[]-based pathnames on POSIX systems and wchar_t[]-based +// pathnames on Windows. +// +// As a precaution against premature truncation, paths can't contain NULs. +// +// Because a FilePath object should not be instantiated at the global scope, +// instead, use a FilePath::CharType[] and initialize it with +// FILE_PATH_LITERAL. At runtime, a FilePath object can be created from the +// character array. Example: +// +// | const FilePath::CharType kLogFileName[] = FILE_PATH_LITERAL("log.txt"); +// | +// | void Function() { +// | FilePath log_file_path(kLogFileName); +// | [...] +// | } +// +// WARNING: FilePaths should ALWAYS be displayed with LTR directionality, even +// when the UI language is RTL. This means you always need to pass filepaths +// through base::i18n::WrapPathWithLTRFormatting() before displaying it in the +// RTL UI. +// +// This is a very common source of bugs, please try to keep this in mind. +// +// ARCANE BITS OF PATH TRIVIA +// +// - A double leading slash is actually part of the POSIX standard. Systems +// are allowed to treat // as an alternate root, as Windows does for UNC +// (network share) paths. Most POSIX systems don't do anything special +// with two leading slashes, but FilePath handles this case properly +// in case it ever comes across such a system. FilePath needs this support +// for Windows UNC paths, anyway. +// References: +// The Open Group Base Specifications Issue 7, sections 3.267 ("Pathname") +// and 4.12 ("Pathname Resolution"), available at: +// http://www.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_267 +// http://www.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_12 +// +// - Windows treats c:\\ the same way it treats \\. This was intended to +// allow older applications that require drive letters to support UNC paths +// like \\server\share\path, by permitting c:\\server\share\path as an +// equivalent. Since the OS treats these paths specially, FilePath needs +// to do the same. Since Windows can use either / or \ as the separator, +// FilePath treats c://, c:\\, //, and \\ all equivalently. +// Reference: +// The Old New Thing, "Why is a drive letter permitted in front of UNC +// paths (sometimes)?", available at: +// http://blogs.msdn.com/oldnewthing/archive/2005/11/22/495740.aspx + +#ifndef BASE_FILES_FILE_PATH_H_ +#define BASE_FILES_FILE_PATH_H_ + +#include + +#include +#include +#include + +#include "base/base_export.h" +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "base/strings/string16.h" +#include "base/strings/string_piece.h" +#include "build/build_config.h" + +// Windows-style drive letter support and pathname separator characters can be +// enabled and disabled independently, to aid testing. These #defines are +// here so that the same setting can be used in both the implementation and +// in the unit test. +#if defined(OS_WIN) +#define FILE_PATH_USES_DRIVE_LETTERS +#define FILE_PATH_USES_WIN_SEPARATORS +#endif // OS_WIN + +// To print path names portably use PRIsFP (based on PRIuS and friends from +// C99 and format_macros.h) like this: +// base::StringPrintf("Path is %" PRIsFP ".\n", path.value().c_str()); +#if defined(OS_WIN) +#define PRIsFP "ls" +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +#define PRIsFP "s" +#endif // OS_WIN + +namespace base { + +class Pickle; +class PickleIterator; + +// An abstraction to isolate users from the differences between native +// pathnames on different platforms. +class BASE_EXPORT FilePath { + public: +#if defined(OS_WIN) + // On Windows, for Unicode-aware applications, native pathnames are wchar_t + // arrays encoded in UTF-16. + typedef std::wstring StringType; +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + // On most platforms, native pathnames are char arrays, and the encoding + // may or may not be specified. On Mac OS X, native pathnames are encoded + // in UTF-8. + typedef std::string StringType; +#endif // OS_WIN + + typedef BasicStringPiece StringPieceType; + typedef StringType::value_type CharType; + + // Null-terminated array of separators used to separate components in + // hierarchical paths. Each character in this array is a valid separator, + // but kSeparators[0] is treated as the canonical separator and will be used + // when composing pathnames. + static const CharType kSeparators[]; + + // arraysize(kSeparators). + static const size_t kSeparatorsLength; + + // A special path component meaning "this directory." + static const CharType kCurrentDirectory[]; + + // A special path component meaning "the parent directory." + static const CharType kParentDirectory[]; + + // The character used to identify a file extension. + static const CharType kExtensionSeparator; + + FilePath(); + FilePath(const FilePath& that); + explicit FilePath(StringPieceType path); + ~FilePath(); + FilePath& operator=(const FilePath& that); + + // Constructs FilePath with the contents of |that|, which is left in valid but + // unspecified state. + FilePath(FilePath&& that) noexcept; + // Replaces the contents with those of |that|, which is left in valid but + // unspecified state. + FilePath& operator=(FilePath&& that); + + bool operator==(const FilePath& that) const; + + bool operator!=(const FilePath& that) const; + + // Required for some STL containers and operations + bool operator<(const FilePath& that) const { + return path_ < that.path_; + } + + const StringType& value() const { return path_; } + + bool empty() const { return path_.empty(); } + + void clear() { path_.clear(); } + + // Returns true if |character| is in kSeparators. + static bool IsSeparator(CharType character); + + // Returns a vector of all of the components of the provided path. It is + // equivalent to calling DirName().value() on the path's root component, + // and BaseName().value() on each child component. + // + // To make sure this is lossless so we can differentiate absolute and + // relative paths, the root slash will be included even though no other + // slashes will be. The precise behavior is: + // + // Posix: "/foo/bar" -> [ "/", "foo", "bar" ] + // Windows: "C:\foo\bar" -> [ "C:", "\\", "foo", "bar" ] + void GetComponents(std::vector* components) const; + + // Returns true if this FilePath is a strict parent of the |child|. Absolute + // and relative paths are accepted i.e. is /foo parent to /foo/bar and + // is foo parent to foo/bar. Does not convert paths to absolute, follow + // symlinks or directory navigation (e.g. ".."). A path is *NOT* its own + // parent. + bool IsParent(const FilePath& child) const; + + // If IsParent(child) holds, appends to path (if non-NULL) the + // relative path to child and returns true. For example, if parent + // holds "/Users/johndoe/Library/Application Support", child holds + // "/Users/johndoe/Library/Application Support/Google/Chrome/Default", and + // *path holds "/Users/johndoe/Library/Caches", then after + // parent.AppendRelativePath(child, path) is called *path will hold + // "/Users/johndoe/Library/Caches/Google/Chrome/Default". Otherwise, + // returns false. + bool AppendRelativePath(const FilePath& child, FilePath* path) const; + + // Returns a FilePath corresponding to the directory containing the path + // named by this object, stripping away the file component. If this object + // only contains one component, returns a FilePath identifying + // kCurrentDirectory. If this object already refers to the root directory, + // returns a FilePath identifying the root directory. Please note that this + // doesn't resolve directory navigation, e.g. the result for "../a" is "..". + FilePath DirName() const WARN_UNUSED_RESULT; + + // Returns a FilePath corresponding to the last path component of this + // object, either a file or a directory. If this object already refers to + // the root directory, returns a FilePath identifying the root directory; + // this is the only situation in which BaseName will return an absolute path. + FilePath BaseName() const WARN_UNUSED_RESULT; + + // Returns ".jpg" for path "C:\pics\jojo.jpg", or an empty string if + // the file has no extension. If non-empty, Extension() will always start + // with precisely one ".". The following code should always work regardless + // of the value of path. For common double-extensions like .tar.gz and + // .user.js, this method returns the combined extension. For a single + // component, use FinalExtension(). + // new_path = path.RemoveExtension().value().append(path.Extension()); + // ASSERT(new_path == path.value()); + // NOTE: this is different from the original file_util implementation which + // returned the extension without a leading "." ("jpg" instead of ".jpg") + StringType Extension() const WARN_UNUSED_RESULT; + + // Returns the path's file extension, as in Extension(), but will + // never return a double extension. + // + // TODO(davidben): Check all our extension-sensitive code to see if + // we can rename this to Extension() and the other to something like + // LongExtension(), defaulting to short extensions and leaving the + // long "extensions" to logic like base::GetUniquePathNumber(). + StringType FinalExtension() const WARN_UNUSED_RESULT; + + // Returns "C:\pics\jojo" for path "C:\pics\jojo.jpg" + // NOTE: this is slightly different from the similar file_util implementation + // which returned simply 'jojo'. + FilePath RemoveExtension() const WARN_UNUSED_RESULT; + + // Removes the path's file extension, as in RemoveExtension(), but + // ignores double extensions. + FilePath RemoveFinalExtension() const WARN_UNUSED_RESULT; + + // Inserts |suffix| after the file name portion of |path| but before the + // extension. Returns "" if BaseName() == "." or "..". + // Examples: + // path == "C:\pics\jojo.jpg" suffix == " (1)", returns "C:\pics\jojo (1).jpg" + // path == "jojo.jpg" suffix == " (1)", returns "jojo (1).jpg" + // path == "C:\pics\jojo" suffix == " (1)", returns "C:\pics\jojo (1)" + // path == "C:\pics.old\jojo" suffix == " (1)", returns "C:\pics.old\jojo (1)" + FilePath InsertBeforeExtension( + StringPieceType suffix) const WARN_UNUSED_RESULT; + FilePath InsertBeforeExtensionASCII( + StringPiece suffix) const WARN_UNUSED_RESULT; + + // Adds |extension| to |file_name|. Returns the current FilePath if + // |extension| is empty. Returns "" if BaseName() == "." or "..". + FilePath AddExtension(StringPieceType extension) const WARN_UNUSED_RESULT; + + // Replaces the extension of |file_name| with |extension|. If |file_name| + // does not have an extension, then |extension| is added. If |extension| is + // empty, then the extension is removed from |file_name|. + // Returns "" if BaseName() == "." or "..". + FilePath ReplaceExtension(StringPieceType extension) const WARN_UNUSED_RESULT; + + // Returns true if the file path matches the specified extension. The test is + // case insensitive. Don't forget the leading period if appropriate. + bool MatchesExtension(StringPieceType extension) const; + + // Returns a FilePath by appending a separator and the supplied path + // component to this object's path. Append takes care to avoid adding + // excessive separators if this object's path already ends with a separator. + // If this object's path is kCurrentDirectory, a new FilePath corresponding + // only to |component| is returned. |component| must be a relative path; + // it is an error to pass an absolute path. + FilePath Append(StringPieceType component) const WARN_UNUSED_RESULT; + FilePath Append(const FilePath& component) const WARN_UNUSED_RESULT; + + // Although Windows StringType is std::wstring, since the encoding it uses for + // paths is well defined, it can handle ASCII path components as well. + // Mac uses UTF8, and since ASCII is a subset of that, it works there as well. + // On Linux, although it can use any 8-bit encoding for paths, we assume that + // ASCII is a valid subset, regardless of the encoding, since many operating + // system paths will always be ASCII. + FilePath AppendASCII(StringPiece component) const WARN_UNUSED_RESULT; + + // Returns true if this FilePath contains an absolute path. On Windows, an + // absolute path begins with either a drive letter specification followed by + // a separator character, or with two separator characters. On POSIX + // platforms, an absolute path begins with a separator character. + bool IsAbsolute() const; + + // Returns true if the patch ends with a path separator character. + bool EndsWithSeparator() const WARN_UNUSED_RESULT; + + // Returns a copy of this FilePath that ends with a trailing separator. If + // the input path is empty, an empty FilePath will be returned. + FilePath AsEndingWithSeparator() const WARN_UNUSED_RESULT; + + // Returns a copy of this FilePath that does not end with a trailing + // separator. + FilePath StripTrailingSeparators() const WARN_UNUSED_RESULT; + + // Returns true if this FilePath contains an attempt to reference a parent + // directory (e.g. has a path component that is ".."). + bool ReferencesParent() const; + + // Return a Unicode human-readable version of this path. + // Warning: you can *not*, in general, go from a display name back to a real + // path. Only use this when displaying paths to users, not just when you + // want to stuff a string16 into some other API. + string16 LossyDisplayName() const; + + // Return the path as ASCII, or the empty string if the path is not ASCII. + // This should only be used for cases where the FilePath is representing a + // known-ASCII filename. + std::string MaybeAsASCII() const; + + // Return the path as UTF-8. + // + // This function is *unsafe* as there is no way to tell what encoding is + // used in file names on POSIX systems other than Mac and Chrome OS, + // although UTF-8 is practically used everywhere these days. To mitigate + // the encoding issue, this function internally calls + // SysNativeMBToWide() on POSIX systems other than Mac and Chrome OS, + // per assumption that the current locale's encoding is used in file + // names, but this isn't a perfect solution. + // + // Once it becomes safe to to stop caring about non-UTF-8 file names, + // the SysNativeMBToWide() hack will be removed from the code, along + // with "Unsafe" in the function name. + std::string AsUTF8Unsafe() const; + + // Similar to AsUTF8Unsafe, but returns UTF-16 instead. + string16 AsUTF16Unsafe() const; + + // Returns a FilePath object from a path name in UTF-8. This function + // should only be used for cases where you are sure that the input + // string is UTF-8. + // + // Like AsUTF8Unsafe(), this function is unsafe. This function + // internally calls SysWideToNativeMB() on POSIX systems other than Mac + // and Chrome OS, to mitigate the encoding issue. See the comment at + // AsUTF8Unsafe() for details. + static FilePath FromUTF8Unsafe(StringPiece utf8); + + // Similar to FromUTF8Unsafe, but accepts UTF-16 instead. + static FilePath FromUTF16Unsafe(StringPiece16 utf16); + + void WriteToPickle(Pickle* pickle) const; + bool ReadFromPickle(PickleIterator* iter); + + // Normalize all path separators to backslash on Windows + // (if FILE_PATH_USES_WIN_SEPARATORS is true), or do nothing on POSIX systems. + FilePath NormalizePathSeparators() const; + + // Normalize all path separattors to given type on Windows + // (if FILE_PATH_USES_WIN_SEPARATORS is true), or do nothing on POSIX systems. + FilePath NormalizePathSeparatorsTo(CharType separator) const; + + // Compare two strings in the same way the file system does. + // Note that these always ignore case, even on file systems that are case- + // sensitive. If case-sensitive comparison is ever needed, add corresponding + // methods here. + // The methods are written as a static method so that they can also be used + // on parts of a file path, e.g., just the extension. + // CompareIgnoreCase() returns -1, 0 or 1 for less-than, equal-to and + // greater-than respectively. + static int CompareIgnoreCase(StringPieceType string1, + StringPieceType string2); + static bool CompareEqualIgnoreCase(StringPieceType string1, + StringPieceType string2) { + return CompareIgnoreCase(string1, string2) == 0; + } + static bool CompareLessIgnoreCase(StringPieceType string1, + StringPieceType string2) { + return CompareIgnoreCase(string1, string2) < 0; + } + +#if defined(OS_MACOSX) + // Returns the string in the special canonical decomposed form as defined for + // HFS, which is close to, but not quite, decomposition form D. See + // http://developer.apple.com/mac/library/technotes/tn/tn1150.html#UnicodeSubtleties + // for further comments. + // Returns the epmty string if the conversion failed. + static StringType GetHFSDecomposedForm(StringPieceType string); + + // Special UTF-8 version of FastUnicodeCompare. Cf: + // http://developer.apple.com/mac/library/technotes/tn/tn1150.html#StringComparisonAlgorithm + // IMPORTANT: The input strings must be in the special HFS decomposed form! + // (cf. above GetHFSDecomposedForm method) + static int HFSFastUnicodeCompare(StringPieceType string1, + StringPieceType string2); +#endif + +#if defined(OS_ANDROID) + // On android, file selection dialog can return a file with content uri + // scheme(starting with content://). Content uri needs to be opened with + // ContentResolver to guarantee that the app has appropriate permissions + // to access it. + // Returns true if the path is a content uri, or false otherwise. + bool IsContentUri() const; +#endif + + private: + // Remove trailing separators from this object. If the path is absolute, it + // will never be stripped any more than to refer to the absolute root + // directory, so "////" will become "/", not "". A leading pair of + // separators is never stripped, to support alternate roots. This is used to + // support UNC paths on Windows. + void StripTrailingSeparatorsInternal(); + + StringType path_; +}; + +BASE_EXPORT std::ostream& operator<<(std::ostream& out, + const FilePath& file_path); + +} // namespace base + +// Macros for string literal initialization of FilePath::CharType[], and for +// using a FilePath::CharType[] in a printf-style format string. +#if defined(OS_WIN) +#define FILE_PATH_LITERAL(x) L ## x +#define PRFilePath "ls" +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +#define FILE_PATH_LITERAL(x) x +#define PRFilePath "s" +#endif // OS_WIN + +namespace std { + +template <> +struct hash { + typedef base::FilePath argument_type; + typedef std::size_t result_type; + result_type operator()(argument_type const& f) const { + return hash()(f.value()); + } +}; + +} // namespace std + +#endif // BASE_FILES_FILE_PATH_H_ diff --git a/native/zucchini/vendor/libchrome/base/files/file_tracing.h b/native/zucchini/vendor/libchrome/base/files/file_tracing.h new file mode 100644 index 0000000..1fbfcd4 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/files/file_tracing.h @@ -0,0 +1,95 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_FILES_FILE_TRACING_H_ +#define BASE_FILES_FILE_TRACING_H_ + +#include + +#include "base/base_export.h" +#include "base/macros.h" + +#define FILE_TRACING_PREFIX "File" + +#define SCOPED_FILE_TRACE_WITH_SIZE(name, size) \ + FileTracing::ScopedTrace scoped_file_trace; \ + if (FileTracing::IsCategoryEnabled()) \ + scoped_file_trace.Initialize(FILE_TRACING_PREFIX "::" name, this, size) + +#define SCOPED_FILE_TRACE(name) SCOPED_FILE_TRACE_WITH_SIZE(name, 0) + +namespace base { + +class File; +class FilePath; + +class BASE_EXPORT FileTracing { + public: + // Whether the file tracing category is enabled. + static bool IsCategoryEnabled(); + + class Provider { + public: + virtual ~Provider() = default; + + // Whether the file tracing category is currently enabled. + virtual bool FileTracingCategoryIsEnabled() const = 0; + + // Enables file tracing for |id|. Must be called before recording events. + virtual void FileTracingEnable(const void* id) = 0; + + // Disables file tracing for |id|. + virtual void FileTracingDisable(const void* id) = 0; + + // Begins an event for |id| with |name|. |path| tells where in the directory + // structure the event is happening (and may be blank). |size| is the number + // of bytes involved in the event. + virtual void FileTracingEventBegin(const char* name, + const void* id, + const FilePath& path, + int64_t size) = 0; + + // Ends an event for |id| with |name|. + virtual void FileTracingEventEnd(const char* name, const void* id) = 0; + }; + + // Sets a global file tracing provider to query categories and record events. + static void SetProvider(Provider* provider); + + // Enables file tracing while in scope. + class ScopedEnabler { + public: + ScopedEnabler(); + ~ScopedEnabler(); + }; + + class ScopedTrace { + public: + ScopedTrace(); + ~ScopedTrace(); + + // Called only if the tracing category is enabled. |name| is the name of the + // event to trace (e.g. "Read", "Write") and must have an application + // lifetime (e.g. static or literal). |file| is the file being traced; must + // outlive this class. |size| is the size (in bytes) of this event. + void Initialize(const char* name, const File* file, int64_t size); + + private: + // The ID of this trace. Based on the |file| passed to |Initialize()|. Must + // outlive this class. + const void* id_; + + // The name of the event to trace (e.g. "Read", "Write"). Prefixed with + // "File". + const char* name_; + + DISALLOW_COPY_AND_ASSIGN(ScopedTrace); + }; + + DISALLOW_COPY_AND_ASSIGN(FileTracing); +}; + +} // namespace base + +#endif // BASE_FILES_FILE_TRACING_H_ diff --git a/native/zucchini/vendor/libchrome/base/files/file_util.h b/native/zucchini/vendor/libchrome/base/files/file_util.h new file mode 100644 index 0000000..456962a --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/files/file_util.h @@ -0,0 +1,498 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file contains utility functions for dealing with the local +// filesystem. + +#ifndef BASE_FILES_FILE_UTIL_H_ +#define BASE_FILES_FILE_UTIL_H_ + +#include +#include +#include + +#include +#include +#include + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) +#include +#include +#endif + +#include "base/base_export.h" +#include "base/files/file.h" +#include "base/files/file_path.h" +#include "base/strings/string16.h" +#include "build/build_config.h" + +#if defined(OS_WIN) +#include "base/win/windows_types.h" +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +#include "base/file_descriptor_posix.h" +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" +#endif + +namespace base { + +class Environment; +class Time; + +//----------------------------------------------------------------------------- +// Functions that involve filesystem access or modification: + +// Returns an absolute version of a relative path. Returns an empty path on +// error. On POSIX, this function fails if the path does not exist. This +// function can result in I/O so it can be slow. +BASE_EXPORT FilePath MakeAbsoluteFilePath(const FilePath& input); + +// Returns the total number of bytes used by all the files under |root_path|. +// If the path does not exist the function returns 0. +// +// This function is implemented using the FileEnumerator class so it is not +// particularly speedy in any platform. +BASE_EXPORT int64_t ComputeDirectorySize(const FilePath& root_path); + +// Deletes the given path, whether it's a file or a directory. +// If it's a directory, it's perfectly happy to delete all of the +// directory's contents. Passing true to recursive deletes +// subdirectories and their contents as well. +// Returns true if successful, false otherwise. It is considered successful +// to attempt to delete a file that does not exist. +// +// In posix environment and if |path| is a symbolic link, this deletes only +// the symlink. (even if the symlink points to a non-existent file) +// +// WARNING: USING THIS WITH recursive==true IS EQUIVALENT +// TO "rm -rf", SO USE WITH CAUTION. +BASE_EXPORT bool DeleteFile(const FilePath& path, bool recursive); + +#if defined(OS_WIN) +// Schedules to delete the given path, whether it's a file or a directory, until +// the operating system is restarted. +// Note: +// 1) The file/directory to be deleted should exist in a temp folder. +// 2) The directory to be deleted must be empty. +BASE_EXPORT bool DeleteFileAfterReboot(const FilePath& path); +#endif + +// Moves the given path, whether it's a file or a directory. +// If a simple rename is not possible, such as in the case where the paths are +// on different volumes, this will attempt to copy and delete. Returns +// true for success. +// This function fails if either path contains traversal components ('..'). +BASE_EXPORT bool Move(const FilePath& from_path, const FilePath& to_path); + +// Renames file |from_path| to |to_path|. Both paths must be on the same +// volume, or the function will fail. Destination file will be created +// if it doesn't exist. Prefer this function over Move when dealing with +// temporary files. On Windows it preserves attributes of the target file. +// Returns true on success, leaving *error unchanged. +// Returns false on failure and sets *error appropriately, if it is non-NULL. +BASE_EXPORT bool ReplaceFile(const FilePath& from_path, + const FilePath& to_path, + File::Error* error); + +// Copies a single file. Use CopyDirectory() to copy directories. +// This function fails if either path contains traversal components ('..'). +// This function also fails if |to_path| is a directory. +// +// On POSIX, if |to_path| is a symlink, CopyFile() will follow the symlink. This +// may have security implications. Use with care. +// +// If |to_path| already exists and is a regular file, it will be overwritten, +// though its permissions will stay the same. +// +// If |to_path| does not exist, it will be created. The new file's permissions +// varies per platform: +// +// - This function keeps the metadata on Windows. The read only bit is not kept. +// - On Mac and iOS, |to_path| retains |from_path|'s permissions, except user +// read/write permissions are always set. +// - On Linux and Android, |to_path| has user read/write permissions only. i.e. +// Always 0600. +// - On ChromeOS, |to_path| has user read/write permissions and group/others +// read permissions. i.e. Always 0644. +BASE_EXPORT bool CopyFile(const FilePath& from_path, const FilePath& to_path); + +// Copies the given path, and optionally all subdirectories and their contents +// as well. +// +// If there are files existing under to_path, always overwrite. Returns true +// if successful, false otherwise. Wildcards on the names are not supported. +// +// This function has the same metadata behavior as CopyFile(). +// +// If you only need to copy a file use CopyFile, it's faster. +BASE_EXPORT bool CopyDirectory(const FilePath& from_path, + const FilePath& to_path, + bool recursive); + +// Like CopyDirectory() except trying to overwrite an existing file will not +// work and will return false. +BASE_EXPORT bool CopyDirectoryExcl(const FilePath& from_path, + const FilePath& to_path, + bool recursive); + +// Returns true if the given path exists on the local filesystem, +// false otherwise. +BASE_EXPORT bool PathExists(const FilePath& path); + +// Returns true if the given path is writable by the user, false otherwise. +BASE_EXPORT bool PathIsWritable(const FilePath& path); + +// Returns true if the given path exists and is a directory, false otherwise. +BASE_EXPORT bool DirectoryExists(const FilePath& path); + +// Returns true if the contents of the two files given are equal, false +// otherwise. If either file can't be read, returns false. +BASE_EXPORT bool ContentsEqual(const FilePath& filename1, + const FilePath& filename2); + +// Returns true if the contents of the two text files given are equal, false +// otherwise. This routine treats "\r\n" and "\n" as equivalent. +BASE_EXPORT bool TextContentsEqual(const FilePath& filename1, + const FilePath& filename2); + +// Reads the file at |path| into |contents| and returns true on success and +// false on error. For security reasons, a |path| containing path traversal +// components ('..') is treated as a read error and |contents| is set to empty. +// In case of I/O error, |contents| holds the data that could be read from the +// file before the error occurred. +// |contents| may be NULL, in which case this function is useful for its side +// effect of priming the disk cache (could be used for unit tests). +BASE_EXPORT bool ReadFileToString(const FilePath& path, std::string* contents); + +// Reads the file at |path| into |contents| and returns true on success and +// false on error. For security reasons, a |path| containing path traversal +// components ('..') is treated as a read error and |contents| is set to empty. +// In case of I/O error, |contents| holds the data that could be read from the +// file before the error occurred. When the file size exceeds |max_size|, the +// function returns false with |contents| holding the file truncated to +// |max_size|. +// |contents| may be NULL, in which case this function is useful for its side +// effect of priming the disk cache (could be used for unit tests). +BASE_EXPORT bool ReadFileToStringWithMaxSize(const FilePath& path, + std::string* contents, + size_t max_size); + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) + +// Read exactly |bytes| bytes from file descriptor |fd|, storing the result +// in |buffer|. This function is protected against EINTR and partial reads. +// Returns true iff |bytes| bytes have been successfully read from |fd|. +BASE_EXPORT bool ReadFromFD(int fd, char* buffer, size_t bytes); + +// Performs the same function as CreateAndOpenTemporaryFileInDir(), but returns +// the file-descriptor directly, rather than wrapping it into a FILE. Returns +// -1 on failure. +BASE_EXPORT int CreateAndOpenFdForTemporaryFileInDir(const FilePath& dir, + FilePath* path); + +#endif // OS_POSIX || OS_FUCHSIA + +#if defined(OS_POSIX) + +// Creates a symbolic link at |symlink| pointing to |target|. Returns +// false on failure. +BASE_EXPORT bool CreateSymbolicLink(const FilePath& target, + const FilePath& symlink); + +// Reads the given |symlink| and returns where it points to in |target|. +// Returns false upon failure. +BASE_EXPORT bool ReadSymbolicLink(const FilePath& symlink, FilePath* target); + +// Bits and masks of the file permission. +enum FilePermissionBits { + FILE_PERMISSION_MASK = S_IRWXU | S_IRWXG | S_IRWXO, + FILE_PERMISSION_USER_MASK = S_IRWXU, + FILE_PERMISSION_GROUP_MASK = S_IRWXG, + FILE_PERMISSION_OTHERS_MASK = S_IRWXO, + + FILE_PERMISSION_READ_BY_USER = S_IRUSR, + FILE_PERMISSION_WRITE_BY_USER = S_IWUSR, + FILE_PERMISSION_EXECUTE_BY_USER = S_IXUSR, + FILE_PERMISSION_READ_BY_GROUP = S_IRGRP, + FILE_PERMISSION_WRITE_BY_GROUP = S_IWGRP, + FILE_PERMISSION_EXECUTE_BY_GROUP = S_IXGRP, + FILE_PERMISSION_READ_BY_OTHERS = S_IROTH, + FILE_PERMISSION_WRITE_BY_OTHERS = S_IWOTH, + FILE_PERMISSION_EXECUTE_BY_OTHERS = S_IXOTH, +}; + +// Reads the permission of the given |path|, storing the file permission +// bits in |mode|. If |path| is symbolic link, |mode| is the permission of +// a file which the symlink points to. +BASE_EXPORT bool GetPosixFilePermissions(const FilePath& path, int* mode); +// Sets the permission of the given |path|. If |path| is symbolic link, sets +// the permission of a file which the symlink points to. +BASE_EXPORT bool SetPosixFilePermissions(const FilePath& path, int mode); + +// Returns true iff |executable| can be found in any directory specified by the +// environment variable in |env|. +BASE_EXPORT bool ExecutableExistsInPath(Environment* env, + const FilePath::StringType& executable); + +#if defined(OS_LINUX) || defined(OS_AIX) +// Determine if files under a given |path| can be mapped and then mprotect'd +// PROT_EXEC. This depends on the mount options used for |path|, which vary +// among different Linux distributions and possibly local configuration. It also +// depends on details of kernel--ChromeOS uses the noexec option for /dev/shm +// but its kernel allows mprotect with PROT_EXEC anyway. +BASE_EXPORT bool IsPathExecutable(const FilePath& path); +#endif // OS_LINUX || OS_AIX + +#endif // OS_POSIX + +// Returns true if the given directory is empty +BASE_EXPORT bool IsDirectoryEmpty(const FilePath& dir_path); + +// Get the temporary directory provided by the system. +// +// WARNING: In general, you should use CreateTemporaryFile variants below +// instead of this function. Those variants will ensure that the proper +// permissions are set so that other users on the system can't edit them while +// they're open (which can lead to security issues). +BASE_EXPORT bool GetTempDir(FilePath* path); + +// Get the home directory. This is more complicated than just getenv("HOME") +// as it knows to fall back on getpwent() etc. +// +// You should not generally call this directly. Instead use DIR_HOME with the +// path service which will use this function but cache the value. +// Path service may also override DIR_HOME. +BASE_EXPORT FilePath GetHomeDir(); + +// Creates a temporary file. The full path is placed in |path|, and the +// function returns true if was successful in creating the file. The file will +// be empty and all handles closed after this function returns. +BASE_EXPORT bool CreateTemporaryFile(FilePath* path); + +// Same as CreateTemporaryFile but the file is created in |dir|. +BASE_EXPORT bool CreateTemporaryFileInDir(const FilePath& dir, + FilePath* temp_file); + +// Create and open a temporary file. File is opened for read/write. +// The full path is placed in |path|. +// Returns a handle to the opened file or NULL if an error occurred. +BASE_EXPORT FILE* CreateAndOpenTemporaryFile(FilePath* path); + +// Similar to CreateAndOpenTemporaryFile, but the file is created in |dir|. +BASE_EXPORT FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, + FilePath* path); + +// Create a new directory. If prefix is provided, the new directory name is in +// the format of prefixyyyy. +// NOTE: prefix is ignored in the POSIX implementation. +// If success, return true and output the full path of the directory created. +BASE_EXPORT bool CreateNewTempDirectory(const FilePath::StringType& prefix, + FilePath* new_temp_path); + +// Create a directory within another directory. +// Extra characters will be appended to |prefix| to ensure that the +// new directory does not have the same name as an existing directory. +BASE_EXPORT bool CreateTemporaryDirInDir(const FilePath& base_dir, + const FilePath::StringType& prefix, + FilePath* new_dir); + +// Creates a directory, as well as creating any parent directories, if they +// don't exist. Returns 'true' on successful creation, or if the directory +// already exists. The directory is only readable by the current user. +// Returns true on success, leaving *error unchanged. +// Returns false on failure and sets *error appropriately, if it is non-NULL. +BASE_EXPORT bool CreateDirectoryAndGetError(const FilePath& full_path, + File::Error* error); + +// Backward-compatible convenience method for the above. +BASE_EXPORT bool CreateDirectory(const FilePath& full_path); + +// Returns the file size. Returns true on success. +BASE_EXPORT bool GetFileSize(const FilePath& file_path, int64_t* file_size); + +// Sets |real_path| to |path| with symbolic links and junctions expanded. +// On windows, make sure the path starts with a lettered drive. +// |path| must reference a file. Function will fail if |path| points to +// a directory or to a nonexistent path. On windows, this function will +// fail if |path| is a junction or symlink that points to an empty file, +// or if |real_path| would be longer than MAX_PATH characters. +BASE_EXPORT bool NormalizeFilePath(const FilePath& path, FilePath* real_path); + +#if defined(OS_WIN) + +// Given a path in NT native form ("\Device\HarddiskVolumeXX\..."), +// return in |drive_letter_path| the equivalent path that starts with +// a drive letter ("C:\..."). Return false if no such path exists. +BASE_EXPORT bool DevicePathToDriveLetterPath(const FilePath& device_path, + FilePath* drive_letter_path); + +// Given an existing file in |path|, set |real_path| to the path +// in native NT format, of the form "\Device\HarddiskVolumeXX\..". +// Returns false if the path can not be found. Empty files cannot +// be resolved with this function. +BASE_EXPORT bool NormalizeToNativeFilePath(const FilePath& path, + FilePath* nt_path); +#endif + +// This function will return if the given file is a symlink or not. +BASE_EXPORT bool IsLink(const FilePath& file_path); + +// Returns information about the given file path. +BASE_EXPORT bool GetFileInfo(const FilePath& file_path, File::Info* info); + +// Sets the time of the last access and the time of the last modification. +BASE_EXPORT bool TouchFile(const FilePath& path, + const Time& last_accessed, + const Time& last_modified); + +// Wrapper for fopen-like calls. Returns non-NULL FILE* on success. The +// underlying file descriptor (POSIX) or handle (Windows) is unconditionally +// configured to not be propagated to child processes. +BASE_EXPORT FILE* OpenFile(const FilePath& filename, const char* mode); + +// Closes file opened by OpenFile. Returns true on success. +BASE_EXPORT bool CloseFile(FILE* file); + +// Associates a standard FILE stream with an existing File. Note that this +// functions take ownership of the existing File. +BASE_EXPORT FILE* FileToFILE(File file, const char* mode); + +// Truncates an open file to end at the location of the current file pointer. +// This is a cross-platform analog to Windows' SetEndOfFile() function. +BASE_EXPORT bool TruncateFile(FILE* file); + +// Reads at most the given number of bytes from the file into the buffer. +// Returns the number of read bytes, or -1 on error. +BASE_EXPORT int ReadFile(const FilePath& filename, char* data, int max_size); + +// Writes the given buffer into the file, overwriting any data that was +// previously there. Returns the number of bytes written, or -1 on error. +BASE_EXPORT int WriteFile(const FilePath& filename, const char* data, + int size); + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) +// Appends |data| to |fd|. Does not close |fd| when done. Returns true iff +// |size| bytes of |data| were written to |fd|. +BASE_EXPORT bool WriteFileDescriptor(const int fd, const char* data, int size); +#endif + +// Appends |data| to |filename|. Returns true iff |size| bytes of |data| were +// written to |filename|. +BASE_EXPORT bool AppendToFile(const FilePath& filename, + const char* data, + int size); + +// Gets the current working directory for the process. +BASE_EXPORT bool GetCurrentDirectory(FilePath* path); + +// Sets the current working directory for the process. +BASE_EXPORT bool SetCurrentDirectory(const FilePath& path); + +// Attempts to find a number that can be appended to the |path| to make it +// unique. If |path| does not exist, 0 is returned. If it fails to find such +// a number, -1 is returned. If |suffix| is not empty, also checks the +// existence of it with the given suffix. +BASE_EXPORT int GetUniquePathNumber(const FilePath& path, + const FilePath::StringType& suffix); + +// Sets the given |fd| to non-blocking mode. +// Returns true if it was able to set it in the non-blocking mode, otherwise +// false. +BASE_EXPORT bool SetNonBlocking(int fd); + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) +// Creates a non-blocking, close-on-exec pipe. +// This creates a non-blocking pipe that is not intended to be shared with any +// child process. This will be done atomically if the operating system supports +// it. Returns true if it was able to create the pipe, otherwise false. +BASE_EXPORT bool CreateLocalNonBlockingPipe(int fds[2]); + +// Sets the given |fd| to close-on-exec mode. +// Returns true if it was able to set it in the close-on-exec mode, otherwise +// false. +BASE_EXPORT bool SetCloseOnExec(int fd); + +// Test that |path| can only be changed by a given user and members of +// a given set of groups. +// Specifically, test that all parts of |path| under (and including) |base|: +// * Exist. +// * Are owned by a specific user. +// * Are not writable by all users. +// * Are owned by a member of a given set of groups, or are not writable by +// their group. +// * Are not symbolic links. +// This is useful for checking that a config file is administrator-controlled. +// |base| must contain |path|. +BASE_EXPORT bool VerifyPathControlledByUser(const base::FilePath& base, + const base::FilePath& path, + uid_t owner_uid, + const std::set& group_gids); +#endif // defined(OS_POSIX) || defined(OS_FUCHSIA) + +#if defined(OS_MACOSX) && !defined(OS_IOS) +// Is |path| writable only by a user with administrator privileges? +// This function uses Mac OS conventions. The super user is assumed to have +// uid 0, and the administrator group is assumed to be named "admin". +// Testing that |path|, and every parent directory including the root of +// the filesystem, are owned by the superuser, controlled by the group +// "admin", are not writable by all users, and contain no symbolic links. +// Will return false if |path| does not exist. +BASE_EXPORT bool VerifyPathControlledByAdmin(const base::FilePath& path); +#endif // defined(OS_MACOSX) && !defined(OS_IOS) + +// Returns the maximum length of path component on the volume containing +// the directory |path|, in the number of FilePath::CharType, or -1 on failure. +BASE_EXPORT int GetMaximumPathComponentLength(const base::FilePath& path); + +#if defined(OS_LINUX) || defined(OS_AIX) +// Broad categories of file systems as returned by statfs() on Linux. +enum FileSystemType { + FILE_SYSTEM_UNKNOWN, // statfs failed. + FILE_SYSTEM_0, // statfs.f_type == 0 means unknown, may indicate AFS. + FILE_SYSTEM_ORDINARY, // on-disk filesystem like ext2 + FILE_SYSTEM_NFS, + FILE_SYSTEM_SMB, + FILE_SYSTEM_CODA, + FILE_SYSTEM_MEMORY, // in-memory file system + FILE_SYSTEM_CGROUP, // cgroup control. + FILE_SYSTEM_OTHER, // any other value. + FILE_SYSTEM_TYPE_COUNT +}; + +// Attempts determine the FileSystemType for |path|. +// Returns false if |path| doesn't exist. +BASE_EXPORT bool GetFileSystemType(const FilePath& path, FileSystemType* type); +#endif + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) +// Get a temporary directory for shared memory files. The directory may depend +// on whether the destination is intended for executable files, which in turn +// depends on how /dev/shmem was mounted. As a result, you must supply whether +// you intend to create executable shmem segments so this function can find +// an appropriate location. +BASE_EXPORT bool GetShmemTempDir(bool executable, FilePath* path); +#endif + +// Internal -------------------------------------------------------------------- + +namespace internal { + +// Same as Move but allows paths with traversal components. +// Use only with extreme care. +BASE_EXPORT bool MoveUnsafe(const FilePath& from_path, + const FilePath& to_path); + +#if defined(OS_WIN) +// Copy from_path to to_path recursively and then delete from_path recursively. +// Returns true if all operations succeed. +// This function simulates Move(), but unlike Move() it works across volumes. +// This function is not transactional. +BASE_EXPORT bool CopyAndDeleteDirectory(const FilePath& from_path, + const FilePath& to_path); +#endif // defined(OS_WIN) + +} // namespace internal +} // namespace base + +#endif // BASE_FILES_FILE_UTIL_H_ diff --git a/native/zucchini/vendor/libchrome/base/files/memory_mapped_file.h b/native/zucchini/vendor/libchrome/base/files/memory_mapped_file.h new file mode 100644 index 0000000..04f4336 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/files/memory_mapped_file.h @@ -0,0 +1,136 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_FILES_MEMORY_MAPPED_FILE_H_ +#define BASE_FILES_MEMORY_MAPPED_FILE_H_ + +#include +#include + +#include "base/base_export.h" +#include "base/files/file.h" +#include "base/macros.h" +#include "build/build_config.h" + +#if defined(OS_WIN) +#include +#endif + +namespace base { + +class FilePath; + +class BASE_EXPORT MemoryMappedFile { + public: + enum Access { + // Mapping a file into memory effectively allows for file I/O on any thread. + // The accessing thread could be paused while data from the file is paged + // into memory. Worse, a corrupted filesystem could cause a SEGV within the + // program instead of just an I/O error. + READ_ONLY, + + // This provides read/write access to a file and must be used with care of + // the additional subtleties involved in doing so. Though the OS will do + // the writing of data on its own time, too many dirty pages can cause + // the OS to pause the thread while it writes them out. The pause can + // be as much as 1s on some systems. + READ_WRITE, + + // This provides read/write access but with the ability to write beyond + // the end of the existing file up to a maximum size specified as the + // "region". Depending on the OS, the file may or may not be immediately + // extended to the maximum size though it won't be loaded in RAM until + // needed. Note, however, that the maximum size will still be reserved + // in the process address space. + READ_WRITE_EXTEND, + }; + + // The default constructor sets all members to invalid/null values. + MemoryMappedFile(); + ~MemoryMappedFile(); + + // Used to hold information about a region [offset + size] of a file. + struct BASE_EXPORT Region { + static const Region kWholeFile; + + bool operator==(const Region& other) const; + bool operator!=(const Region& other) const; + + // Start of the region (measured in bytes from the beginning of the file). + int64_t offset; + + // Length of the region in bytes. + size_t size; + }; + + // Opens an existing file and maps it into memory. |access| can be read-only + // or read/write but not read/write+extend. If this object already points + // to a valid memory mapped file then this method will fail and return + // false. If it cannot open the file, the file does not exist, or the + // memory mapping fails, it will return false. + bool Initialize(const FilePath& file_name, Access access); + bool Initialize(const FilePath& file_name) { + return Initialize(file_name, READ_ONLY); + } + + // As above, but works with an already-opened file. |access| can be read-only + // or read/write but not read/write+extend. MemoryMappedFile takes ownership + // of |file| and closes it when done. |file| must have been opened with + // permissions suitable for |access|. If the memory mapping fails, it will + // return false. + bool Initialize(File file, Access access); + bool Initialize(File file) { + return Initialize(std::move(file), READ_ONLY); + } + + // As above, but works with a region of an already-opened file. All forms of + // |access| are allowed. If READ_WRITE_EXTEND is specified then |region| + // provides the maximum size of the file. If the memory mapping fails, it + // return false. + bool Initialize(File file, const Region& region, Access access); + bool Initialize(File file, const Region& region) { + return Initialize(std::move(file), region, READ_ONLY); + } + + const uint8_t* data() const { return data_; } + uint8_t* data() { return data_; } + size_t length() const { return length_; } + + // Is file_ a valid file handle that points to an open, memory mapped file? + bool IsValid() const; + + private: + // Given the arbitrarily aligned memory region [start, size], returns the + // boundaries of the region aligned to the granularity specified by the OS, + // (a page on Linux, ~32k on Windows) as follows: + // - |aligned_start| is page aligned and <= |start|. + // - |aligned_size| is a multiple of the VM granularity and >= |size|. + // - |offset| is the displacement of |start| w.r.t |aligned_start|. + static void CalculateVMAlignedBoundaries(int64_t start, + size_t size, + int64_t* aligned_start, + size_t* aligned_size, + int32_t* offset); + + // Map the file to memory, set data_ to that memory address. Return true on + // success, false on any kind of failure. This is a helper for Initialize(). + bool MapFileRegionToMemory(const Region& region, Access access); + + // Closes all open handles. + void CloseHandles(); + + File file_; + uint8_t* data_; + size_t length_; + +#if defined(OS_WIN) + win::ScopedHandle file_mapping_; +#endif + + DISALLOW_COPY_AND_ASSIGN(MemoryMappedFile); +}; + +} // namespace base + +#endif // BASE_FILES_MEMORY_MAPPED_FILE_H_ diff --git a/native/zucchini/vendor/libchrome/base/files/platform_file.h b/native/zucchini/vendor/libchrome/base/files/platform_file.h new file mode 100644 index 0000000..3929a0d --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/files/platform_file.h @@ -0,0 +1,43 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_FILES_PLATFORM_FILE_H_ +#define BASE_FILES_PLATFORM_FILE_H_ + +#include "base/files/scoped_file.h" +#include "build/build_config.h" + +#if defined(OS_WIN) +#include "base/win/scoped_handle.h" +#include "base/win/windows_types.h" +#endif + +// This file defines platform-independent types for dealing with +// platform-dependent files. If possible, use the higher-level base::File class +// rather than these primitives. + +namespace base { + +#if defined(OS_WIN) + +using PlatformFile = HANDLE; +using ScopedPlatformFile = ::base::win::ScopedHandle; + +// It would be nice to make this constexpr but INVALID_HANDLE_VALUE is a +// ((void*)(-1)) which Clang rejects since reinterpret_cast is technically +// disallowed in constexpr. Visual Studio accepts this, however. +const PlatformFile kInvalidPlatformFile = INVALID_HANDLE_VALUE; + +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + +using PlatformFile = int; +using ScopedPlatformFile = ::base::ScopedFD; + +constexpr PlatformFile kInvalidPlatformFile = -1; + +#endif + +} // namespace + +#endif // BASE_FILES_PLATFORM_FILE_H_ diff --git a/native/zucchini/vendor/libchrome/base/files/scoped_file.h b/native/zucchini/vendor/libchrome/base/files/scoped_file.h new file mode 100644 index 0000000..e32a603 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/files/scoped_file.h @@ -0,0 +1,62 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_FILES_SCOPED_FILE_H_ +#define BASE_FILES_SCOPED_FILE_H_ + +#include + +#include + +#include "base/base_export.h" +#include "base/logging.h" +#include "base/scoped_generic.h" +#include "build/build_config.h" + +namespace base { + +namespace internal { + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) +struct BASE_EXPORT ScopedFDCloseTraits { + static int InvalidValue() { + return -1; + } + static void Free(int fd); +}; +#endif + +// Functor for |ScopedFILE| (below). +struct ScopedFILECloser { + inline void operator()(FILE* x) const { + if (x) + fclose(x); + } +}; + +} // namespace internal + +// ----------------------------------------------------------------------------- + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) +// A low-level Posix file descriptor closer class. Use this when writing +// platform-specific code, especially that does non-file-like things with the +// FD (like sockets). +// +// If you're writing low-level Windows code, see base/win/scoped_handle.h +// which provides some additional functionality. +// +// If you're writing cross-platform code that deals with actual files, you +// should generally use base::File instead which can be constructed with a +// handle, and in addition to handling ownership, has convenient cross-platform +// file manipulation functions on it. +typedef ScopedGeneric ScopedFD; +#endif + +// Automatically closes |FILE*|s. +typedef std::unique_ptr ScopedFILE; + +} // namespace base + +#endif // BASE_FILES_SCOPED_FILE_H_ diff --git a/native/zucchini/vendor/libchrome/base/format_macros.h b/native/zucchini/vendor/libchrome/base/format_macros.h new file mode 100644 index 0000000..1279ff7 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/format_macros.h @@ -0,0 +1,97 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_FORMAT_MACROS_H_ +#define BASE_FORMAT_MACROS_H_ + +// This file defines the format macros for some integer types. + +// To print a 64-bit value in a portable way: +// int64_t value; +// printf("xyz:%" PRId64, value); +// The "d" in the macro corresponds to %d; you can also use PRIu64 etc. +// +// For wide strings, prepend "Wide" to the macro: +// int64_t value; +// StringPrintf(L"xyz: %" WidePRId64, value); +// +// To print a size_t value in a portable way: +// size_t size; +// printf("xyz: %" PRIuS, size); +// The "u" in the macro corresponds to %u, and S is for "size". + +#include +#include + +#include "build/build_config.h" + +#if (defined(OS_POSIX) || defined(OS_FUCHSIA)) && \ + (defined(_INTTYPES_H) || defined(_INTTYPES_H_)) && !defined(PRId64) +#error "inttypes.h has already been included before this header file, but " +#error "without __STDC_FORMAT_MACROS defined." +#endif + +#if (defined(OS_POSIX) || defined(OS_FUCHSIA)) && !defined(__STDC_FORMAT_MACROS) +#define __STDC_FORMAT_MACROS +#endif + +#include + +#if defined(OS_WIN) + +#if !defined(PRId64) || !defined(PRIu64) || !defined(PRIx64) +#error "inttypes.h provided by win toolchain should define these." +#endif + +#define WidePRId64 L"I64d" +#define WidePRIu64 L"I64u" +#define WidePRIx64 L"I64x" + +#if !defined(PRIuS) +#define PRIuS "Iu" +#endif + +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + +// GCC will concatenate wide and narrow strings correctly, so nothing needs to +// be done here. +#define WidePRId64 PRId64 +#define WidePRIu64 PRIu64 +#define WidePRIx64 PRIx64 + +#if !defined(PRIuS) +#define PRIuS "zu" +#endif + +#endif // defined(OS_WIN) + +// The size of NSInteger and NSUInteger varies between 32-bit and 64-bit +// architectures and Apple does not provides standard format macros and +// recommends casting. This has many drawbacks, so instead define macros +// for formatting those types. +#if defined(OS_MACOSX) +#if defined(ARCH_CPU_64_BITS) +#if !defined(PRIdNS) +#define PRIdNS "ld" +#endif +#if !defined(PRIuNS) +#define PRIuNS "lu" +#endif +#if !defined(PRIxNS) +#define PRIxNS "lx" +#endif +#else // defined(ARCH_CPU_64_BITS) +#if !defined(PRIdNS) +#define PRIdNS "d" +#endif +#if !defined(PRIuNS) +#define PRIuNS "u" +#endif +#if !defined(PRIxNS) +#define PRIxNS "x" +#endif +#endif +#endif // defined(OS_MACOSX) + +#endif // BASE_FORMAT_MACROS_H_ diff --git a/native/zucchini/vendor/libchrome/base/gtest_prod_util.h b/native/zucchini/vendor/libchrome/base/gtest_prod_util.h new file mode 100644 index 0000000..2ca267e --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/gtest_prod_util.h @@ -0,0 +1,66 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_GTEST_PROD_UTIL_H_ +#define BASE_GTEST_PROD_UTIL_H_ + +#include "testing/gtest/include/gtest/gtest_prod.h" // nogncheck + +// This is a wrapper for gtest's FRIEND_TEST macro that friends +// test with all possible prefixes. This is very helpful when changing the test +// prefix, because the friend declarations don't need to be updated. +// +// Example usage: +// +// class MyClass { +// private: +// void MyMethod(); +// FRIEND_TEST_ALL_PREFIXES(MyClassTest, MyMethod); +// }; +#define FRIEND_TEST_ALL_PREFIXES(test_case_name, test_name) \ + FRIEND_TEST(test_case_name, test_name); \ + FRIEND_TEST(test_case_name, DISABLED_##test_name); \ + FRIEND_TEST(test_case_name, FLAKY_##test_name) + +// C++ compilers will refuse to compile the following code: +// +// namespace foo { +// class MyClass { +// private: +// FRIEND_TEST_ALL_PREFIXES(MyClassTest, TestMethod); +// bool private_var; +// }; +// } // namespace foo +// +// class MyClassTest::TestMethod() { +// foo::MyClass foo_class; +// foo_class.private_var = true; +// } +// +// Unless you forward declare MyClassTest::TestMethod outside of namespace foo. +// Use FORWARD_DECLARE_TEST to do so for all possible prefixes. +// +// Example usage: +// +// FORWARD_DECLARE_TEST(MyClassTest, TestMethod); +// +// namespace foo { +// class MyClass { +// private: +// FRIEND_TEST_ALL_PREFIXES(::MyClassTest, TestMethod); // NOTE use of :: +// bool private_var; +// }; +// } // namespace foo +// +// class MyClassTest::TestMethod() { +// foo::MyClass foo_class; +// foo_class.private_var = true; +// } + +#define FORWARD_DECLARE_TEST(test_case_name, test_name) \ + class test_case_name##_##test_name##_Test; \ + class test_case_name##_##DISABLED_##test_name##_Test; \ + class test_case_name##_##FLAKY_##test_name##_Test + +#endif // BASE_GTEST_PROD_UTIL_H_ diff --git a/native/zucchini/vendor/libchrome/base/hash.h b/native/zucchini/vendor/libchrome/base/hash.h new file mode 100644 index 0000000..165899e --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/hash.h @@ -0,0 +1,69 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_HASH_H_ +#define BASE_HASH_H_ + +#include +#include + +#include +#include +#include + +#include "base/base_export.h" +#include "base/logging.h" +#include "base/strings/string16.h" + +namespace base { + +// Computes a hash of a memory buffer. This hash function is subject to change +// in the future, so use only for temporary in-memory structures. If you need +// to persist a change on disk or between computers, use PersistentHash(). +// +// WARNING: This hash function should not be used for any cryptographic purpose. +BASE_EXPORT uint32_t Hash(const void* data, size_t length); +BASE_EXPORT uint32_t Hash(const std::string& str); +BASE_EXPORT uint32_t Hash(const string16& str); + +// Computes a hash of a memory buffer. This hash function must not change so +// that code can use the hashed values for persistent storage purposes or +// sending across the network. If a new persistent hash function is desired, a +// new version will have to be added in addition. +// +// WARNING: This hash function should not be used for any cryptographic purpose. +BASE_EXPORT uint32_t PersistentHash(const void* data, size_t length); +BASE_EXPORT uint32_t PersistentHash(const std::string& str); + +// Hash pairs of 32-bit or 64-bit numbers. +BASE_EXPORT size_t HashInts32(uint32_t value1, uint32_t value2); +BASE_EXPORT size_t HashInts64(uint64_t value1, uint64_t value2); + +template +inline size_t HashInts(T1 value1, T2 value2) { + // This condition is expected to be compile-time evaluated and optimised away + // in release builds. + if (sizeof(T1) > sizeof(uint32_t) || (sizeof(T2) > sizeof(uint32_t))) + return HashInts64(value1, value2); + + return HashInts32(value1, value2); +} + +// A templated hasher for pairs of integer types. Example: +// +// using MyPair = std::pair; +// std::unordered_set> set; +template +struct IntPairHash; + +template +struct IntPairHash> { + size_t operator()(std::pair value) const { + return HashInts(value.first, value.second); + } +}; + +} // namespace base + +#endif // BASE_HASH_H_ diff --git a/native/zucchini/vendor/libchrome/base/lazy_instance.h b/native/zucchini/vendor/libchrome/base/lazy_instance.h new file mode 100644 index 0000000..4449373 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/lazy_instance.h @@ -0,0 +1,210 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DEPRECATED !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// Please don't introduce new instances of LazyInstance. Use a function-local +// static of type base::NoDestructor instead: +// +// Factory& Factory::GetInstance() { +// static base::NoDestructor instance; +// return *instance; +// } +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// +// The LazyInstance class manages a single instance of Type, +// which will be lazily created on the first time it's accessed. This class is +// useful for places you would normally use a function-level static, but you +// need to have guaranteed thread-safety. The Type constructor will only ever +// be called once, even if two threads are racing to create the object. Get() +// and Pointer() will always return the same, completely initialized instance. +// When the instance is constructed it is registered with AtExitManager. The +// destructor will be called on program exit. +// +// LazyInstance is completely thread safe, assuming that you create it safely. +// The class was designed to be POD initialized, so it shouldn't require a +// static constructor. It really only makes sense to declare a LazyInstance as +// a global variable using the LAZY_INSTANCE_INITIALIZER initializer. +// +// LazyInstance is similar to Singleton, except it does not have the singleton +// property. You can have multiple LazyInstance's of the same type, and each +// will manage a unique instance. It also preallocates the space for Type, as +// to avoid allocating the Type instance on the heap. This may help with the +// performance of creating the instance, and reducing heap fragmentation. This +// requires that Type be a complete type so we can determine the size. +// +// Example usage: +// static LazyInstance::Leaky inst = LAZY_INSTANCE_INITIALIZER; +// void SomeMethod() { +// inst.Get().SomeMethod(); // MyClass::SomeMethod() +// +// MyClass* ptr = inst.Pointer(); +// ptr->DoDoDo(); // MyClass::DoDoDo +// } + +#ifndef BASE_LAZY_INSTANCE_H_ +#define BASE_LAZY_INSTANCE_H_ + +#include // For placement new. + +#include "base/atomicops.h" +#include "base/debug/leak_annotations.h" +#include "base/lazy_instance_helpers.h" +#include "base/logging.h" +#include "base/threading/thread_restrictions.h" + +// LazyInstance uses its own struct initializer-list style static +// initialization, which does not require a constructor. +#define LAZY_INSTANCE_INITIALIZER {} + +namespace base { + +template +struct LazyInstanceTraitsBase { + static Type* New(void* instance) { + DCHECK_EQ(reinterpret_cast(instance) & (alignof(Type) - 1), 0u); + // Use placement new to initialize our instance in our preallocated space. + // The parenthesis is very important here to force POD type initialization. + return new (instance) Type(); + } + + static void CallDestructor(Type* instance) { + // Explicitly call the destructor. + instance->~Type(); + } +}; + +// We pull out some of the functionality into non-templated functions, so we +// can implement the more complicated pieces out of line in the .cc file. +namespace internal { + +// This traits class causes destruction the contained Type at process exit via +// AtExitManager. This is probably generally not what you want. Instead, prefer +// Leaky below. +template +struct DestructorAtExitLazyInstanceTraits { + static const bool kRegisterOnExit = true; +#if DCHECK_IS_ON() + static const bool kAllowedToAccessOnNonjoinableThread = false; +#endif + + static Type* New(void* instance) { + return LazyInstanceTraitsBase::New(instance); + } + + static void Delete(Type* instance) { + LazyInstanceTraitsBase::CallDestructor(instance); + } +}; + +// Use LazyInstance::Leaky for a less-verbose call-site typedef; e.g.: +// base::LazyInstance::Leaky my_leaky_lazy_instance; +// instead of: +// base::LazyInstance > +// my_leaky_lazy_instance; +// (especially when T is MyLongTypeNameImplClientHolderFactory). +// Only use this internal::-qualified verbose form to extend this traits class +// (depending on its implementation details). +template +struct LeakyLazyInstanceTraits { + static const bool kRegisterOnExit = false; +#if DCHECK_IS_ON() + static const bool kAllowedToAccessOnNonjoinableThread = true; +#endif + + static Type* New(void* instance) { + ANNOTATE_SCOPED_MEMORY_LEAK; + return LazyInstanceTraitsBase::New(instance); + } + static void Delete(Type* instance) { + } +}; + +template +struct ErrorMustSelectLazyOrDestructorAtExitForLazyInstance {}; + +} // namespace internal + +template < + typename Type, + typename Traits = + internal::ErrorMustSelectLazyOrDestructorAtExitForLazyInstance> +class LazyInstance { + public: + // Do not define a destructor, as doing so makes LazyInstance a + // non-POD-struct. We don't want that because then a static initializer will + // be created to register the (empty) destructor with atexit() under MSVC, for + // example. We handle destruction of the contained Type class explicitly via + // the OnExit member function, where needed. + // ~LazyInstance() {} + + // Convenience typedef to avoid having to repeat Type for leaky lazy + // instances. + typedef LazyInstance> Leaky; + typedef LazyInstance> + DestructorAtExit; + + Type& Get() { + return *Pointer(); + } + + Type* Pointer() { +#if DCHECK_IS_ON() + if (!Traits::kAllowedToAccessOnNonjoinableThread) + ThreadRestrictions::AssertSingletonAllowed(); +#endif + + return subtle::GetOrCreateLazyPointer( + &private_instance_, &Traits::New, private_buf_, + Traits::kRegisterOnExit ? OnExit : nullptr, this); + } + + // Returns true if the lazy instance has been created. Unlike Get() and + // Pointer(), calling IsCreated() will not instantiate the object of Type. + bool IsCreated() { + // Return true (i.e. "created") if |private_instance_| is either being + // created right now (i.e. |private_instance_| has value of + // internal::kLazyInstanceStateCreating) or was already created (i.e. + // |private_instance_| has any other non-zero value). + return 0 != subtle::NoBarrier_Load(&private_instance_); + } + + // MSVC gives a warning that the alignment expands the size of the + // LazyInstance struct to make the size a multiple of the alignment. This + // is expected in this case. +#if defined(OS_WIN) +#pragma warning(push) +#pragma warning(disable: 4324) +#endif + + // Effectively private: member data is only public to allow the linker to + // statically initialize it and to maintain a POD class. DO NOT USE FROM + // OUTSIDE THIS CLASS. + subtle::AtomicWord private_instance_; + + // Preallocated space for the Type instance. + alignas(Type) char private_buf_[sizeof(Type)]; + +#if defined(OS_WIN) +#pragma warning(pop) +#endif + + private: + Type* instance() { + return reinterpret_cast(subtle::NoBarrier_Load(&private_instance_)); + } + + // Adapter function for use with AtExit. This should be called single + // threaded, so don't synchronize across threads. + // Calling OnExit while the instance is in use by other threads is a mistake. + static void OnExit(void* lazy_instance) { + LazyInstance* me = + reinterpret_cast*>(lazy_instance); + Traits::Delete(me->instance()); + subtle::NoBarrier_Store(&me->private_instance_, 0); + } +}; + +} // namespace base + +#endif // BASE_LAZY_INSTANCE_H_ diff --git a/native/zucchini/vendor/libchrome/base/lazy_instance_helpers.cc b/native/zucchini/vendor/libchrome/base/lazy_instance_helpers.cc new file mode 100644 index 0000000..7b9e0de --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/lazy_instance_helpers.cc @@ -0,0 +1,64 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/lazy_instance_helpers.h" + +#include "base/at_exit.h" +#include "base/atomicops.h" +#include "base/threading/platform_thread.h" + +namespace base { +namespace internal { + +bool NeedsLazyInstance(subtle::AtomicWord* state) { + // Try to create the instance, if we're the first, will go from 0 to + // kLazyInstanceStateCreating, otherwise we've already been beaten here. + // The memory access has no memory ordering as state 0 and + // kLazyInstanceStateCreating have no associated data (memory barriers are + // all about ordering of memory accesses to *associated* data). + if (subtle::NoBarrier_CompareAndSwap(state, 0, kLazyInstanceStateCreating) == + 0) { + // Caller must create instance + return true; + } + + // It's either in the process of being created, or already created. Spin. + // The load has acquire memory ordering as a thread which sees + // state_ == STATE_CREATED needs to acquire visibility over + // the associated data (buf_). Pairing Release_Store is in + // CompleteLazyInstance(). + if (subtle::Acquire_Load(state) == kLazyInstanceStateCreating) { + const base::TimeTicks start = base::TimeTicks::Now(); + do { + const base::TimeDelta elapsed = base::TimeTicks::Now() - start; + // Spin with YieldCurrentThread for at most one ms - this ensures maximum + // responsiveness. After that spin with Sleep(1ms) so that we don't burn + // excessive CPU time - this also avoids infinite loops due to priority + // inversions (https://crbug.com/797129). + if (elapsed < TimeDelta::FromMilliseconds(1)) + PlatformThread::YieldCurrentThread(); + else + PlatformThread::Sleep(TimeDelta::FromMilliseconds(1)); + } while (subtle::Acquire_Load(state) == kLazyInstanceStateCreating); + } + // Someone else created the instance. + return false; +} + +void CompleteLazyInstance(subtle::AtomicWord* state, + subtle::AtomicWord new_instance, + void (*destructor)(void*), + void* destructor_arg) { + // Instance is created, go from CREATING to CREATED (or reset it if + // |new_instance| is null). Releases visibility over |private_buf_| to + // readers. Pairing Acquire_Load is in NeedsLazyInstance(). + subtle::Release_Store(state, new_instance); + + // Make sure that the lazily instantiated object will get destroyed at exit. + if (new_instance && destructor) + AtExitManager::RegisterCallback(destructor, destructor_arg); +} + +} // namespace internal +} // namespace base diff --git a/native/zucchini/vendor/libchrome/base/lazy_instance_helpers.h b/native/zucchini/vendor/libchrome/base/lazy_instance_helpers.h new file mode 100644 index 0000000..5a43d8b --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/lazy_instance_helpers.h @@ -0,0 +1,101 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_LAZY_INSTANCE_INTERNAL_H_ +#define BASE_LAZY_INSTANCE_INTERNAL_H_ + +#include "base/atomicops.h" +#include "base/base_export.h" +#include "base/logging.h" + +// Helper methods used by LazyInstance and a few other base APIs for thread-safe +// lazy construction. + +namespace base { +namespace internal { + +// Our AtomicWord doubles as a spinlock, where a value of +// kLazyInstanceStateCreating means the spinlock is being held for creation. +constexpr subtle::AtomicWord kLazyInstanceStateCreating = 1; + +// Helper for GetOrCreateLazyPointer(). Checks if instance needs to be created. +// If so returns true otherwise if another thread has beat us, waits for +// instance to be created and returns false. +BASE_EXPORT bool NeedsLazyInstance(subtle::AtomicWord* state); + +// Helper for GetOrCreateLazyPointer(). After creating an instance, this is +// called to register the dtor to be called at program exit and to update the +// atomic state to hold the |new_instance| +BASE_EXPORT void CompleteLazyInstance(subtle::AtomicWord* state, + subtle::AtomicWord new_instance, + void (*destructor)(void*), + void* destructor_arg); + +} // namespace internal + +namespace subtle { + +// If |state| is uninitialized (zero), constructs a value using +// |creator_func(creator_arg)|, stores it into |state| and registers +// |destructor(destructor_arg)| to be called when the current AtExitManager goes +// out of scope. Then, returns the value stored in |state|. It is safe to have +// concurrent calls to this function with the same |state|. |creator_func| may +// return nullptr if it doesn't want to create an instance anymore (e.g. on +// shutdown), it is from then on required to return nullptr to all callers (ref. +// StaticMemorySingletonTraits). In that case, callers need to synchronize +// before |creator_func| may return a non-null instance again (ref. +// StaticMemorySingletonTraits::ResurectForTesting()). +// Implementation note on |creator_func/creator_arg|. It makes for ugly adapters +// but it avoids redundant template instantiations (e.g. saves 27KB in +// chrome.dll) because linker is able to fold these for multiple Types but +// couldn't with the more advanced CreatorFunc template type which in turn +// improves code locality (and application startup) -- ref. +// https://chromium-review.googlesource.com/c/chromium/src/+/530984/5/base/lazy_instance.h#140, +// worsened by https://chromium-review.googlesource.com/c/chromium/src/+/868013 +// and caught then as https://crbug.com/804034. +template +Type* GetOrCreateLazyPointer(subtle::AtomicWord* state, + Type* (*creator_func)(void*), + void* creator_arg, + void (*destructor)(void*), + void* destructor_arg) { + DCHECK(state); + DCHECK(creator_func); + + // If any bit in the created mask is true, the instance has already been + // fully constructed. + constexpr subtle::AtomicWord kLazyInstanceCreatedMask = + ~internal::kLazyInstanceStateCreating; + + // We will hopefully have fast access when the instance is already created. + // Since a thread sees |state| == 0 or kLazyInstanceStateCreating at most + // once, the load is taken out of NeedsLazyInstance() as a fast-path. The load + // has acquire memory ordering as a thread which sees |state| > creating needs + // to acquire visibility over the associated data. Pairing Release_Store is in + // CompleteLazyInstance(). + subtle::AtomicWord instance = subtle::Acquire_Load(state); + if (!(instance & kLazyInstanceCreatedMask)) { + if (internal::NeedsLazyInstance(state)) { + // This thread won the race and is now responsible for creating the + // instance and storing it back into |state|. + instance = + reinterpret_cast((*creator_func)(creator_arg)); + internal::CompleteLazyInstance(state, instance, destructor, + destructor_arg); + } else { + // This thread lost the race but now has visibility over the constructed + // instance (NeedsLazyInstance() doesn't return until the constructing + // thread releases the instance via CompleteLazyInstance()). + instance = subtle::Acquire_Load(state); + DCHECK(instance & kLazyInstanceCreatedMask); + } + } + return reinterpret_cast(instance); +} + +} // namespace subtle + +} // namespace base + +#endif // BASE_LAZY_INSTANCE_INTERNAL_H_ diff --git a/native/zucchini/vendor/libchrome/base/location.cc b/native/zucchini/vendor/libchrome/base/location.cc new file mode 100644 index 0000000..8bbf6ed --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/location.cc @@ -0,0 +1,77 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/location.h" + +#if defined(COMPILER_MSVC) +#include +#endif + +#include "base/compiler_specific.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" +#include "build/build_config.h" + +namespace base { + +Location::Location() = default; +Location::Location(const Location& other) = default; + +Location::Location(const char* file_name, const void* program_counter) + : file_name_(file_name), program_counter_(program_counter) {} + +Location::Location(const char* function_name, + const char* file_name, + int line_number, + const void* program_counter) + : function_name_(function_name), + file_name_(file_name), + line_number_(line_number), + program_counter_(program_counter) { +#if !defined(OS_NACL) + // The program counter should not be null except in a default constructed + // (empty) Location object. This value is used for identity, so if it doesn't + // uniquely identify a location, things will break. + // + // The program counter isn't supported in NaCl so location objects won't work + // properly in that context. + DCHECK(program_counter); +#endif +} + +std::string Location::ToString() const { + if (has_source_info()) { + return std::string(function_name_) + "@" + file_name_ + ":" + + IntToString(line_number_); + } + return StringPrintf("pc:%p", program_counter_); +} + +#if defined(COMPILER_MSVC) +#define RETURN_ADDRESS() _ReturnAddress() +#elif defined(COMPILER_GCC) && !defined(OS_NACL) +#define RETURN_ADDRESS() \ + __builtin_extract_return_addr(__builtin_return_address(0)) +#else +#define RETURN_ADDRESS() nullptr +#endif + +// static +NOINLINE Location Location::CreateFromHere(const char* file_name) { + return Location(file_name, RETURN_ADDRESS()); +} + +// static +NOINLINE Location Location::CreateFromHere(const char* function_name, + const char* file_name, + int line_number) { + return Location(function_name, file_name, line_number, RETURN_ADDRESS()); +} + +//------------------------------------------------------------------------------ +NOINLINE const void* GetProgramCounter() { + return RETURN_ADDRESS(); +} + +} // namespace base diff --git a/native/zucchini/vendor/libchrome/base/location.h b/native/zucchini/vendor/libchrome/base/location.h new file mode 100644 index 0000000..14fe2fa --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/location.h @@ -0,0 +1,117 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_LOCATION_H_ +#define BASE_LOCATION_H_ + +#include + +#include +#include + +#include "base/base_export.h" +#include "base/debug/debugging_buildflags.h" +#include "base/hash.h" + +namespace base { + +// Location provides basic info where of an object was constructed, or was +// significantly brought to life. +class BASE_EXPORT Location { + public: + Location(); + Location(const Location& other); + + // Only initializes the file name and program counter, the source information + // will be null for the strings, and -1 for the line number. + // TODO(http://crbug.com/760702) remove file name from this constructor. + Location(const char* file_name, const void* program_counter); + + // Constructor should be called with a long-lived char*, such as __FILE__. + // It assumes the provided value will persist as a global constant, and it + // will not make a copy of it. + Location(const char* function_name, + const char* file_name, + int line_number, + const void* program_counter); + + // Comparator for hash map insertion. The program counter should uniquely + // identify a location. + bool operator==(const Location& other) const { + return program_counter_ == other.program_counter_; + } + + // Returns true if there is source code location info. If this is false, + // the Location object only contains a program counter or is + // default-initialized (the program counter is also null). + bool has_source_info() const { return function_name_ && file_name_; } + + // Will be nullptr for default initialized Location objects and when source + // names are disabled. + const char* function_name() const { return function_name_; } + + // Will be nullptr for default initialized Location objects and when source + // names are disabled. + const char* file_name() const { return file_name_; } + + // Will be -1 for default initialized Location objects and when source names + // are disabled. + int line_number() const { return line_number_; } + + // The address of the code generating this Location object. Should always be + // valid except for default initialized Location objects, which will be + // nullptr. + const void* program_counter() const { return program_counter_; } + + // Converts to the most user-readable form possible. If function and filename + // are not available, this will return "pc:". + std::string ToString() const; + + static Location CreateFromHere(const char* file_name); + static Location CreateFromHere(const char* function_name, + const char* file_name, + int line_number); + + private: + const char* function_name_ = nullptr; + const char* file_name_ = nullptr; + int line_number_ = -1; + const void* program_counter_ = nullptr; +}; + +BASE_EXPORT const void* GetProgramCounter(); + +// The macros defined here will expand to the current function. +#if BUILDFLAG(ENABLE_LOCATION_SOURCE) + +// Full source information should be included. +#define FROM_HERE FROM_HERE_WITH_EXPLICIT_FUNCTION(__func__) +#define FROM_HERE_WITH_EXPLICIT_FUNCTION(function_name) \ + ::base::Location::CreateFromHere(function_name, __FILE__, __LINE__) + +#else + +// TODO(http://crbug.com/760702) remove the __FILE__ argument from these calls. +#define FROM_HERE ::base::Location::CreateFromHere(__FILE__) +#define FROM_HERE_WITH_EXPLICIT_FUNCTION(function_name) \ + ::base::Location::CreateFromHere(function_name, __FILE__, -1) + +#endif + +} // namespace base + +namespace std { + +// Specialization for using Location in hash tables. +template <> +struct hash<::base::Location> { + std::size_t operator()(const ::base::Location& loc) const { + const void* program_counter = loc.program_counter(); + return base::Hash(&program_counter, sizeof(void*)); + } +}; + +} // namespace std + +#endif // BASE_LOCATION_H_ diff --git a/native/zucchini/vendor/libchrome/base/logging.cc b/native/zucchini/vendor/libchrome/base/logging.cc new file mode 100644 index 0000000..5781c63 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/logging.cc @@ -0,0 +1,1065 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" + +#include +#include + +#include "base/macros.h" +#include "build/build_config.h" + +#if defined(OS_WIN) +#include +#include +typedef HANDLE FileHandle; +typedef HANDLE MutexHandle; +// Windows warns on using write(). It prefers _write(). +#define write(fd, buf, count) _write(fd, buf, static_cast(count)) +// Windows doesn't define STDERR_FILENO. Define it here. +#define STDERR_FILENO 2 + +#elif defined(OS_MACOSX) +// In MacOS 10.12 and iOS 10.0 and later ASL (Apple System Log) was deprecated +// in favor of OS_LOG (Unified Logging). +#include +#if defined(OS_IOS) +#if !defined(__IPHONE_10_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_10_0 +#define USE_ASL +#endif +#else // !defined(OS_IOS) +#if !defined(MAC_OS_X_VERSION_10_12) || \ + MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12 +#define USE_ASL +#endif +#endif // defined(OS_IOS) + +#if defined(USE_ASL) +#include +#else +#include +#endif + +#include +#include +#include +#include + +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +#if defined(OS_NACL) +#include // timespec doesn't seem to be in +#endif +#include +#endif + +#if defined(OS_FUCHSIA) +#include +#include +#endif + +#if defined(OS_ANDROID) +#include +#endif + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) +#include +#include +#include +#include +#include +#include +#include +#include +#define MAX_PATH PATH_MAX +typedef FILE* FileHandle; +typedef pthread_mutex_t* MutexHandle; +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "base/base_switches.h" +#include "base/callback.h" +#include "base/command_line.h" +#include "base/containers/stack.h" +#include "base/debug/activity_tracker.h" +#include "base/debug/alias.h" +#include "base/debug/debugger.h" +#include "base/debug/stack_trace.h" +#include "base/lazy_instance.h" +#include "base/posix/eintr_wrapper.h" +#include "base/strings/string_piece.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/strings/sys_string_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "base/synchronization/lock_impl.h" +#include "base/threading/platform_thread.h" +#include "base/vlog.h" + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) +#include "base/posix/safe_strerror.h" +#endif + +namespace logging { + +namespace { + +VlogInfo* g_vlog_info = nullptr; +VlogInfo* g_vlog_info_prev = nullptr; + +const char* const log_severity_names[] = {"INFO", "WARNING", "ERROR", "FATAL"}; +static_assert(LOG_NUM_SEVERITIES == arraysize(log_severity_names), + "Incorrect number of log_severity_names"); + +const char* log_severity_name(int severity) { + if (severity >= 0 && severity < LOG_NUM_SEVERITIES) + return log_severity_names[severity]; + return "UNKNOWN"; +} + +int g_min_log_level = 0; + +LoggingDestination g_logging_destination = LOG_DEFAULT; + +// For LOG_ERROR and above, always print to stderr. +const int kAlwaysPrintErrorLevel = LOG_ERROR; + +// Which log file to use? This is initialized by InitLogging or +// will be lazily initialized to the default value when it is +// first needed. +#if defined(OS_WIN) +typedef std::wstring PathString; +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +typedef std::string PathString; +#endif +PathString* g_log_file_name = nullptr; + +// This file is lazily opened and the handle may be nullptr +FileHandle g_log_file = nullptr; + +// What should be prepended to each message? +bool g_log_process_id = false; +bool g_log_thread_id = false; +bool g_log_timestamp = true; +bool g_log_tickcount = false; + +// Should we pop up fatal debug messages in a dialog? +bool show_error_dialogs = false; + +// An assert handler override specified by the client to be called instead of +// the debug message dialog and process termination. Assert handlers are stored +// in stack to allow overriding and restoring. +base::LazyInstance>::Leaky + log_assert_handler_stack = LAZY_INSTANCE_INITIALIZER; + +// A log message handler that gets notified of every log message we process. +LogMessageHandlerFunction log_message_handler = nullptr; + +// Helper functions to wrap platform differences. + +int32_t CurrentProcessId() { +#if defined(OS_WIN) + return GetCurrentProcessId(); +#elif defined(OS_FUCHSIA) + zx_info_handle_basic_t basic = {}; + zx_object_get_info(zx_process_self(), ZX_INFO_HANDLE_BASIC, &basic, + sizeof(basic), nullptr, nullptr); + return basic.koid; +#elif defined(OS_POSIX) + return getpid(); +#endif +} + +uint64_t TickCount() { +#if defined(OS_WIN) + return GetTickCount(); +#elif defined(OS_FUCHSIA) + return zx_clock_get(ZX_CLOCK_MONOTONIC) / + static_cast(base::Time::kNanosecondsPerMicrosecond); +#elif defined(OS_MACOSX) + return mach_absolute_time(); +#elif defined(OS_NACL) + // NaCl sadly does not have _POSIX_TIMERS enabled in sys/features.h + // So we have to use clock() for now. + return clock(); +#elif defined(OS_POSIX) + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + + uint64_t absolute_micro = static_cast(ts.tv_sec) * 1000000 + + static_cast(ts.tv_nsec) / 1000; + + return absolute_micro; +#endif +} + +void DeleteFilePath(const PathString& log_name) { +#if defined(OS_WIN) + DeleteFile(log_name.c_str()); +#elif defined(OS_NACL) + // Do nothing; unlink() isn't supported on NaCl. +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + unlink(log_name.c_str()); +#else +#error Unsupported platform +#endif +} + +PathString GetDefaultLogFile() { +#if defined(OS_WIN) + // On Windows we use the same path as the exe. + wchar_t module_name[MAX_PATH]; + GetModuleFileName(nullptr, module_name, MAX_PATH); + + PathString log_name = module_name; + PathString::size_type last_backslash = log_name.rfind('\\', log_name.size()); + if (last_backslash != PathString::npos) + log_name.erase(last_backslash + 1); + log_name += L"debug.log"; + return log_name; +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + // On other platforms we just use the current directory. + return PathString("debug.log"); +#endif +} + +// We don't need locks on Windows for atomically appending to files. The OS +// provides this functionality. +#if defined(OS_POSIX) || defined(OS_FUCHSIA) +// This class acts as a wrapper for locking the logging files. +// LoggingLock::Init() should be called from the main thread before any logging +// is done. Then whenever logging, be sure to have a local LoggingLock +// instance on the stack. This will ensure that the lock is unlocked upon +// exiting the frame. +// LoggingLocks can not be nested. +class LoggingLock { + public: + LoggingLock() { + LockLogging(); + } + + ~LoggingLock() { + UnlockLogging(); + } + + static void Init(LogLockingState lock_log, const PathChar* new_log_file) { + if (initialized) + return; + lock_log_file = lock_log; + + if (lock_log_file != LOCK_LOG_FILE) + log_lock = new base::internal::LockImpl(); + + initialized = true; + } + + private: + static void LockLogging() { + if (lock_log_file == LOCK_LOG_FILE) { + pthread_mutex_lock(&log_mutex); + } else { + // use the lock + log_lock->Lock(); + } + } + + static void UnlockLogging() { + if (lock_log_file == LOCK_LOG_FILE) { + pthread_mutex_unlock(&log_mutex); + } else { + log_lock->Unlock(); + } + } + + // The lock is used if log file locking is false. It helps us avoid problems + // with multiple threads writing to the log file at the same time. Use + // LockImpl directly instead of using Lock, because Lock makes logging calls. + static base::internal::LockImpl* log_lock; + + // When we don't use a lock, we are using a global mutex. We need to do this + // because LockFileEx is not thread safe. + static pthread_mutex_t log_mutex; + + static bool initialized; + static LogLockingState lock_log_file; +}; + +// static +bool LoggingLock::initialized = false; +// static +base::internal::LockImpl* LoggingLock::log_lock = nullptr; +// static +LogLockingState LoggingLock::lock_log_file = LOCK_LOG_FILE; + +pthread_mutex_t LoggingLock::log_mutex = PTHREAD_MUTEX_INITIALIZER; + +#endif // OS_POSIX || OS_FUCHSIA + +// Called by logging functions to ensure that |g_log_file| is initialized +// and can be used for writing. Returns false if the file could not be +// initialized. |g_log_file| will be nullptr in this case. +bool InitializeLogFileHandle() { + if (g_log_file) + return true; + + if (!g_log_file_name) { + // Nobody has called InitLogging to specify a debug log file, so here we + // initialize the log file name to a default. + g_log_file_name = new PathString(GetDefaultLogFile()); + } + + if ((g_logging_destination & LOG_TO_FILE) != 0) { +#if defined(OS_WIN) + // The FILE_APPEND_DATA access mask ensures that the file is atomically + // appended to across accesses from multiple threads. + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364399(v=vs.85).aspx + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx + g_log_file = CreateFile(g_log_file_name->c_str(), FILE_APPEND_DATA, + FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + if (g_log_file == INVALID_HANDLE_VALUE || g_log_file == nullptr) { + // We are intentionally not using FilePath or FileUtil here to reduce the + // dependencies of the logging implementation. For e.g. FilePath and + // FileUtil depend on shell32 and user32.dll. This is not acceptable for + // some consumers of base logging like chrome_elf, etc. + // Please don't change the code below to use FilePath. + // try the current directory + wchar_t system_buffer[MAX_PATH]; + system_buffer[0] = 0; + DWORD len = ::GetCurrentDirectory(arraysize(system_buffer), + system_buffer); + if (len == 0 || len > arraysize(system_buffer)) + return false; + + *g_log_file_name = system_buffer; + // Append a trailing backslash if needed. + if (g_log_file_name->back() != L'\\') + *g_log_file_name += L"\\"; + *g_log_file_name += L"debug.log"; + + g_log_file = CreateFile(g_log_file_name->c_str(), FILE_APPEND_DATA, + FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + if (g_log_file == INVALID_HANDLE_VALUE || g_log_file == nullptr) { + g_log_file = nullptr; + return false; + } + } +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + g_log_file = fopen(g_log_file_name->c_str(), "a"); + if (g_log_file == nullptr) + return false; +#else +#error Unsupported platform +#endif + } + + return true; +} + +void CloseFile(FileHandle log) { +#if defined(OS_WIN) + CloseHandle(log); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + fclose(log); +#else +#error Unsupported platform +#endif +} + +void CloseLogFileUnlocked() { + if (!g_log_file) + return; + + CloseFile(g_log_file); + g_log_file = nullptr; +} + +} // namespace + +#if DCHECK_IS_CONFIGURABLE +// In DCHECK-enabled Chrome builds, allow the meaning of LOG_DCHECK to be +// determined at run-time. We default it to INFO, to avoid it triggering +// crashes before the run-time has explicitly chosen the behaviour. +BASE_EXPORT logging::LogSeverity LOG_DCHECK = LOG_INFO; +#endif // DCHECK_IS_CONFIGURABLE + +// This is never instantiated, it's just used for EAT_STREAM_PARAMETERS to have +// an object of the correct type on the LHS of the unused part of the ternary +// operator. +std::ostream* g_swallow_stream; + +LoggingSettings::LoggingSettings() + : logging_dest(LOG_DEFAULT), + log_file(nullptr), + lock_log(LOCK_LOG_FILE), + delete_old(APPEND_TO_OLD_LOG_FILE) {} + +bool BaseInitLoggingImpl(const LoggingSettings& settings) { +#if defined(OS_NACL) + // Can log only to the system debug log. + CHECK_EQ(settings.logging_dest & ~LOG_TO_SYSTEM_DEBUG_LOG, 0); +#endif + if (base::CommandLine::InitializedForCurrentProcess()) { + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + // Don't bother initializing |g_vlog_info| unless we use one of the + // vlog switches. + if (command_line->HasSwitch(switches::kV) || + command_line->HasSwitch(switches::kVModule)) { + // NOTE: If |g_vlog_info| has already been initialized, it might be in use + // by another thread. Don't delete the old VLogInfo, just create a second + // one. We keep track of both to avoid memory leak warnings. + CHECK(!g_vlog_info_prev); + g_vlog_info_prev = g_vlog_info; + + g_vlog_info = + new VlogInfo(command_line->GetSwitchValueASCII(switches::kV), + command_line->GetSwitchValueASCII(switches::kVModule), + &g_min_log_level); + } + } + + g_logging_destination = settings.logging_dest; + + // ignore file options unless logging to file is set. + if ((g_logging_destination & LOG_TO_FILE) == 0) + return true; + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) + LoggingLock::Init(settings.lock_log, settings.log_file); + LoggingLock logging_lock; +#endif + + // Calling InitLogging twice or after some log call has already opened the + // default log file will re-initialize to the new options. + CloseLogFileUnlocked(); + + if (!g_log_file_name) + g_log_file_name = new PathString(); + *g_log_file_name = settings.log_file; + if (settings.delete_old == DELETE_OLD_LOG_FILE) + DeleteFilePath(*g_log_file_name); + + return InitializeLogFileHandle(); +} + +void SetMinLogLevel(int level) { + g_min_log_level = std::min(LOG_FATAL, level); +} + +int GetMinLogLevel() { + return g_min_log_level; +} + +bool ShouldCreateLogMessage(int severity) { + if (severity < g_min_log_level) + return false; + + // Return true here unless we know ~LogMessage won't do anything. Note that + // ~LogMessage writes to stderr if severity_ >= kAlwaysPrintErrorLevel, even + // when g_logging_destination is LOG_NONE. + return g_logging_destination != LOG_NONE || log_message_handler || + severity >= kAlwaysPrintErrorLevel; +} + +int GetVlogVerbosity() { + return std::max(-1, LOG_INFO - GetMinLogLevel()); +} + +int GetVlogLevelHelper(const char* file, size_t N) { + DCHECK_GT(N, 0U); + // Note: |g_vlog_info| may change on a different thread during startup + // (but will always be valid or nullptr). + VlogInfo* vlog_info = g_vlog_info; + return vlog_info ? + vlog_info->GetVlogLevel(base::StringPiece(file, N - 1)) : + GetVlogVerbosity(); +} + +void SetLogItems(bool enable_process_id, bool enable_thread_id, + bool enable_timestamp, bool enable_tickcount) { + g_log_process_id = enable_process_id; + g_log_thread_id = enable_thread_id; + g_log_timestamp = enable_timestamp; + g_log_tickcount = enable_tickcount; +} + +void SetShowErrorDialogs(bool enable_dialogs) { + show_error_dialogs = enable_dialogs; +} + +ScopedLogAssertHandler::ScopedLogAssertHandler( + LogAssertHandlerFunction handler) { + log_assert_handler_stack.Get().push(std::move(handler)); +} + +ScopedLogAssertHandler::~ScopedLogAssertHandler() { + log_assert_handler_stack.Get().pop(); +} + +void SetLogMessageHandler(LogMessageHandlerFunction handler) { + log_message_handler = handler; +} + +LogMessageHandlerFunction GetLogMessageHandler() { + return log_message_handler; +} + +// Explicit instantiations for commonly used comparisons. +template std::string* MakeCheckOpString( + const int&, const int&, const char* names); +template std::string* MakeCheckOpString( + const unsigned long&, const unsigned long&, const char* names); +template std::string* MakeCheckOpString( + const unsigned long&, const unsigned int&, const char* names); +template std::string* MakeCheckOpString( + const unsigned int&, const unsigned long&, const char* names); +template std::string* MakeCheckOpString( + const std::string&, const std::string&, const char* name); + +void MakeCheckOpValueString(std::ostream* os, std::nullptr_t p) { + (*os) << "nullptr"; +} + +#if !defined(NDEBUG) +// Displays a message box to the user with the error message in it. +// Used for fatal messages, where we close the app simultaneously. +// This is for developers only; we don't use this in circumstances +// (like release builds) where users could see it, since users don't +// understand these messages anyway. +void DisplayDebugMessageInDialog(const std::string& str) { + if (str.empty()) + return; + + if (!show_error_dialogs) + return; + +#if defined(OS_WIN) + // We intentionally don't implement a dialog on other platforms. + // You can just look at stderr. + MessageBoxW(nullptr, base::UTF8ToUTF16(str).c_str(), L"Fatal error", + MB_OK | MB_ICONHAND | MB_TOPMOST); +#endif // defined(OS_WIN) +} +#endif // !defined(NDEBUG) + +#if defined(OS_WIN) +LogMessage::SaveLastError::SaveLastError() : last_error_(::GetLastError()) { +} + +LogMessage::SaveLastError::~SaveLastError() { + ::SetLastError(last_error_); +} +#endif // defined(OS_WIN) + +LogMessage::LogMessage(const char* file, int line, LogSeverity severity) + : severity_(severity), file_(file), line_(line) { + Init(file, line); +} + +LogMessage::LogMessage(const char* file, int line, const char* condition) + : severity_(LOG_FATAL), file_(file), line_(line) { + Init(file, line); + stream_ << "Check failed: " << condition << ". "; +} + +LogMessage::LogMessage(const char* file, int line, std::string* result) + : severity_(LOG_FATAL), file_(file), line_(line) { + Init(file, line); + stream_ << "Check failed: " << *result; + delete result; +} + +LogMessage::LogMessage(const char* file, int line, LogSeverity severity, + std::string* result) + : severity_(severity), file_(file), line_(line) { + Init(file, line); + stream_ << "Check failed: " << *result; + delete result; +} + +LogMessage::~LogMessage() { + size_t stack_start = stream_.tellp(); +#if !defined(OFFICIAL_BUILD) && !defined(OS_NACL) && !defined(__UCLIBC__) && \ + !defined(OS_AIX) + if (severity_ == LOG_FATAL && !base::debug::BeingDebugged()) { + // Include a stack trace on a fatal, unless a debugger is attached. + base::debug::StackTrace trace; + stream_ << std::endl; // Newline to separate from log message. + trace.OutputToStream(&stream_); + } +#endif + stream_ << std::endl; + std::string str_newline(stream_.str()); + + // Give any log message handler first dibs on the message. + if (log_message_handler && + log_message_handler(severity_, file_, line_, + message_start_, str_newline)) { + // The handler took care of it, no further processing. + return; + } + + if ((g_logging_destination & LOG_TO_SYSTEM_DEBUG_LOG) != 0) { +#if defined(OS_WIN) + OutputDebugStringA(str_newline.c_str()); +#elif defined(OS_MACOSX) + // In LOG_TO_SYSTEM_DEBUG_LOG mode, log messages are always written to + // stderr. If stderr is /dev/null, also log via ASL (Apple System Log) or + // its successor OS_LOG. If there's something weird about stderr, assume + // that log messages are going nowhere and log via ASL/OS_LOG too. + // Messages logged via ASL/OS_LOG show up in Console.app. + // + // Programs started by launchd, as UI applications normally are, have had + // stderr connected to /dev/null since OS X 10.8. Prior to that, stderr was + // a pipe to launchd, which logged what it received (see log_redirect_fd in + // 10.7.5 launchd-392.39/launchd/src/launchd_core_logic.c). + // + // Another alternative would be to determine whether stderr is a pipe to + // launchd and avoid logging via ASL only in that case. See 10.7.5 + // CF-635.21/CFUtilities.c also_do_stderr(). This would result in logging to + // both stderr and ASL/OS_LOG even in tests, where it's undesirable to log + // to the system log at all. + // + // Note that the ASL client by default discards messages whose levels are + // below ASL_LEVEL_NOTICE. It's possible to change that with + // asl_set_filter(), but this is pointless because syslogd normally applies + // the same filter. + const bool log_to_system = []() { + struct stat stderr_stat; + if (fstat(fileno(stderr), &stderr_stat) == -1) { + return true; + } + if (!S_ISCHR(stderr_stat.st_mode)) { + return false; + } + + struct stat dev_null_stat; + if (stat(_PATH_DEVNULL, &dev_null_stat) == -1) { + return true; + } + + return !S_ISCHR(dev_null_stat.st_mode) || + stderr_stat.st_rdev == dev_null_stat.st_rdev; + }(); + + if (log_to_system) { + // Log roughly the same way that CFLog() and NSLog() would. See 10.10.5 + // CF-1153.18/CFUtilities.c __CFLogCString(). + CFBundleRef main_bundle = CFBundleGetMainBundle(); + CFStringRef main_bundle_id_cf = + main_bundle ? CFBundleGetIdentifier(main_bundle) : nullptr; + std::string main_bundle_id = + main_bundle_id_cf ? base::SysCFStringRefToUTF8(main_bundle_id_cf) + : std::string(""); +#if defined(USE_ASL) + // The facility is set to the main bundle ID if available. Otherwise, + // "com.apple.console" is used. + const class ASLClient { + public: + explicit ASLClient(const std::string& facility) + : client_(asl_open(nullptr, facility.c_str(), ASL_OPT_NO_DELAY)) {} + ~ASLClient() { asl_close(client_); } + + aslclient get() const { return client_; } + + private: + aslclient client_; + DISALLOW_COPY_AND_ASSIGN(ASLClient); + } asl_client(main_bundle_id.empty() ? main_bundle_id + : "com.apple.console"); + + const class ASLMessage { + public: + ASLMessage() : message_(asl_new(ASL_TYPE_MSG)) {} + ~ASLMessage() { asl_free(message_); } + + aslmsg get() const { return message_; } + + private: + aslmsg message_; + DISALLOW_COPY_AND_ASSIGN(ASLMessage); + } asl_message; + + // By default, messages are only readable by the admin group. Explicitly + // make them readable by the user generating the messages. + char euid_string[12]; + snprintf(euid_string, arraysize(euid_string), "%d", geteuid()); + asl_set(asl_message.get(), ASL_KEY_READ_UID, euid_string); + + // Map Chrome log severities to ASL log levels. + const char* const asl_level_string = [](LogSeverity severity) { + // ASL_LEVEL_* are ints, but ASL needs equivalent strings. This + // non-obvious two-step macro trick achieves what's needed. + // https://gcc.gnu.org/onlinedocs/cpp/Stringification.html +#define ASL_LEVEL_STR(level) ASL_LEVEL_STR_X(level) +#define ASL_LEVEL_STR_X(level) #level + switch (severity) { + case LOG_INFO: + return ASL_LEVEL_STR(ASL_LEVEL_INFO); + case LOG_WARNING: + return ASL_LEVEL_STR(ASL_LEVEL_WARNING); + case LOG_ERROR: + return ASL_LEVEL_STR(ASL_LEVEL_ERR); + case LOG_FATAL: + return ASL_LEVEL_STR(ASL_LEVEL_CRIT); + default: + return severity < 0 ? ASL_LEVEL_STR(ASL_LEVEL_DEBUG) + : ASL_LEVEL_STR(ASL_LEVEL_NOTICE); + } +#undef ASL_LEVEL_STR +#undef ASL_LEVEL_STR_X + }(severity_); + asl_set(asl_message.get(), ASL_KEY_LEVEL, asl_level_string); + + asl_set(asl_message.get(), ASL_KEY_MSG, str_newline.c_str()); + + asl_send(asl_client.get(), asl_message.get()); +#else // !defined(USE_ASL) + const class OSLog { + public: + explicit OSLog(const char* subsystem) + : os_log_(subsystem ? os_log_create(subsystem, "chromium_logging") + : OS_LOG_DEFAULT) {} + ~OSLog() { + if (os_log_ != OS_LOG_DEFAULT) { + os_release(os_log_); + } + } + os_log_t get() const { return os_log_; } + + private: + os_log_t os_log_; + DISALLOW_COPY_AND_ASSIGN(OSLog); + } log(main_bundle_id.empty() ? nullptr : main_bundle_id.c_str()); + const os_log_type_t os_log_type = [](LogSeverity severity) { + switch (severity) { + case LOG_INFO: + return OS_LOG_TYPE_INFO; + case LOG_WARNING: + return OS_LOG_TYPE_DEFAULT; + case LOG_ERROR: + return OS_LOG_TYPE_ERROR; + case LOG_FATAL: + return OS_LOG_TYPE_FAULT; + default: + return severity < 0 ? OS_LOG_TYPE_DEBUG : OS_LOG_TYPE_DEFAULT; + } + }(severity_); + os_log_with_type(log.get(), os_log_type, "%{public}s", + str_newline.c_str()); +#endif // defined(USE_ASL) + } +#elif defined(OS_ANDROID) + android_LogPriority priority = + (severity_ < 0) ? ANDROID_LOG_VERBOSE : ANDROID_LOG_UNKNOWN; + switch (severity_) { + case LOG_INFO: + priority = ANDROID_LOG_INFO; + break; + case LOG_WARNING: + priority = ANDROID_LOG_WARN; + break; + case LOG_ERROR: + priority = ANDROID_LOG_ERROR; + break; + case LOG_FATAL: + priority = ANDROID_LOG_FATAL; + break; + } +#if 0 // This is for building Chromium browser on Android. + __android_log_write(priority, "chromium", str_newline.c_str()); +#else + __android_log_write( + priority, + base::CommandLine::InitializedForCurrentProcess() ? + base::CommandLine::ForCurrentProcess()-> + GetProgram().BaseName().value().c_str() : nullptr, + str_newline.c_str()); +#endif // 0 +#endif // defined(OS_ANDROID) + ignore_result(fwrite(str_newline.data(), str_newline.size(), 1, stderr)); + fflush(stderr); + } else if (severity_ >= kAlwaysPrintErrorLevel) { + // When we're only outputting to a log file, above a certain log level, we + // should still output to stderr so that we can better detect and diagnose + // problems with unit tests, especially on the buildbots. + ignore_result(fwrite(str_newline.data(), str_newline.size(), 1, stderr)); + fflush(stderr); + } + + // write to log file + if ((g_logging_destination & LOG_TO_FILE) != 0) { + // We can have multiple threads and/or processes, so try to prevent them + // from clobbering each other's writes. + // If the client app did not call InitLogging, and the lock has not + // been created do it now. We do this on demand, but if two threads try + // to do this at the same time, there will be a race condition to create + // the lock. This is why InitLogging should be called from the main + // thread at the beginning of execution. +#if defined(OS_POSIX) || defined(OS_FUCHSIA) + LoggingLock::Init(LOCK_LOG_FILE, nullptr); + LoggingLock logging_lock; +#endif + if (InitializeLogFileHandle()) { +#if defined(OS_WIN) + DWORD num_written; + WriteFile(g_log_file, + static_cast(str_newline.c_str()), + static_cast(str_newline.length()), + &num_written, + nullptr); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + ignore_result(fwrite( + str_newline.data(), str_newline.size(), 1, g_log_file)); + fflush(g_log_file); +#else +#error Unsupported platform +#endif + } + } + + if (severity_ == LOG_FATAL) { + // Write the log message to the global activity tracker, if running. + base::debug::GlobalActivityTracker* tracker = + base::debug::GlobalActivityTracker::Get(); + if (tracker) + tracker->RecordLogMessage(str_newline); + + // Ensure the first characters of the string are on the stack so they + // are contained in minidumps for diagnostic purposes. + DEBUG_ALIAS_FOR_CSTR(str_stack, str_newline.c_str(), 1024); + + if (log_assert_handler_stack.IsCreated() && + !log_assert_handler_stack.Get().empty()) { + LogAssertHandlerFunction log_assert_handler = + log_assert_handler_stack.Get().top(); + + if (log_assert_handler) { + log_assert_handler.Run( + file_, line_, + base::StringPiece(str_newline.c_str() + message_start_, + stack_start - message_start_), + base::StringPiece(str_newline.c_str() + stack_start)); + } + } else { + // Don't use the string with the newline, get a fresh version to send to + // the debug message process. We also don't display assertions to the + // user in release mode. The enduser can't do anything with this + // information, and displaying message boxes when the application is + // hosed can cause additional problems. +#ifndef NDEBUG + if (!base::debug::BeingDebugged()) { + // Displaying a dialog is unnecessary when debugging and can complicate + // debugging. + DisplayDebugMessageInDialog(stream_.str()); + } +#endif + // Crash the process to generate a dump. + base::debug::BreakDebugger(); + } + } +} + +// writes the common header info to the stream +void LogMessage::Init(const char* file, int line) { + base::StringPiece filename(file); + size_t last_slash_pos = filename.find_last_of("\\/"); + if (last_slash_pos != base::StringPiece::npos) + filename.remove_prefix(last_slash_pos + 1); + + // TODO(darin): It might be nice if the columns were fixed width. + + stream_ << '['; + if (g_log_process_id) + stream_ << CurrentProcessId() << ':'; + if (g_log_thread_id) + stream_ << base::PlatformThread::CurrentId() << ':'; + if (g_log_timestamp) { +#if defined(OS_WIN) + SYSTEMTIME local_time; + GetLocalTime(&local_time); + stream_ << std::setfill('0') + << std::setw(2) << local_time.wMonth + << std::setw(2) << local_time.wDay + << '/' + << std::setw(2) << local_time.wHour + << std::setw(2) << local_time.wMinute + << std::setw(2) << local_time.wSecond + << '.' + << std::setw(3) + << local_time.wMilliseconds + << ':'; +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + timeval tv; + gettimeofday(&tv, nullptr); + time_t t = tv.tv_sec; + struct tm local_time; + localtime_r(&t, &local_time); + struct tm* tm_time = &local_time; + stream_ << std::setfill('0') + << std::setw(2) << 1 + tm_time->tm_mon + << std::setw(2) << tm_time->tm_mday + << '/' + << std::setw(2) << tm_time->tm_hour + << std::setw(2) << tm_time->tm_min + << std::setw(2) << tm_time->tm_sec + << '.' + << std::setw(6) << tv.tv_usec + << ':'; +#else +#error Unsupported platform +#endif + } + if (g_log_tickcount) + stream_ << TickCount() << ':'; + if (severity_ >= 0) + stream_ << log_severity_name(severity_); + else + stream_ << "VERBOSE" << -severity_; + + stream_ << ":" << filename << "(" << line << ")] "; + + message_start_ = stream_.str().length(); +} + +#if defined(OS_WIN) +// This has already been defined in the header, but defining it again as DWORD +// ensures that the type used in the header is equivalent to DWORD. If not, +// the redefinition is a compile error. +typedef DWORD SystemErrorCode; +#endif + +SystemErrorCode GetLastSystemErrorCode() { +#if defined(OS_WIN) + return ::GetLastError(); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + return errno; +#endif +} + +BASE_EXPORT std::string SystemErrorCodeToString(SystemErrorCode error_code) { +#if defined(OS_WIN) + const int kErrorMessageBufferSize = 256; + char msgbuf[kErrorMessageBufferSize]; + DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; + DWORD len = FormatMessageA(flags, nullptr, error_code, 0, msgbuf, + arraysize(msgbuf), nullptr); + if (len) { + // Messages returned by system end with line breaks. + return base::CollapseWhitespaceASCII(msgbuf, true) + + base::StringPrintf(" (0x%lX)", error_code); + } + return base::StringPrintf("Error (0x%lX) while retrieving error. (0x%lX)", + GetLastError(), error_code); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + return base::safe_strerror(error_code) + + base::StringPrintf(" (%d)", error_code); +#endif // defined(OS_WIN) +} + + +#if defined(OS_WIN) +Win32ErrorLogMessage::Win32ErrorLogMessage(const char* file, + int line, + LogSeverity severity, + SystemErrorCode err) + : err_(err), + log_message_(file, line, severity) { +} + +Win32ErrorLogMessage::~Win32ErrorLogMessage() { + stream() << ": " << SystemErrorCodeToString(err_); + // We're about to crash (CHECK). Put |err_| on the stack (by placing it in a + // field) and use Alias in hopes that it makes it into crash dumps. + DWORD last_error = err_; + base::debug::Alias(&last_error); +} +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +ErrnoLogMessage::ErrnoLogMessage(const char* file, + int line, + LogSeverity severity, + SystemErrorCode err) + : err_(err), + log_message_(file, line, severity) { +} + +ErrnoLogMessage::~ErrnoLogMessage() { + stream() << ": " << SystemErrorCodeToString(err_); + // We're about to crash (CHECK). Put |err_| on the stack (by placing it in a + // field) and use Alias in hopes that it makes it into crash dumps. + int last_error = err_; + base::debug::Alias(&last_error); +} +#endif // defined(OS_WIN) + +void CloseLogFile() { +#if defined(OS_POSIX) || defined(OS_FUCHSIA) + LoggingLock logging_lock; +#endif + CloseLogFileUnlocked(); +} + +void RawLog(int level, const char* message) { + if (level >= g_min_log_level && message) { + size_t bytes_written = 0; + const size_t message_len = strlen(message); + int rv; + while (bytes_written < message_len) { + rv = HANDLE_EINTR( + write(STDERR_FILENO, message + bytes_written, + message_len - bytes_written)); + if (rv < 0) { + // Give up, nothing we can do now. + break; + } + bytes_written += rv; + } + + if (message_len > 0 && message[message_len - 1] != '\n') { + do { + rv = HANDLE_EINTR(write(STDERR_FILENO, "\n", 1)); + if (rv < 0) { + // Give up, nothing we can do now. + break; + } + } while (rv != 1); + } + } + + if (level == LOG_FATAL) + base::debug::BreakDebugger(); +} + +// This was defined at the beginning of this file. +#undef write + +#if defined(OS_WIN) +bool IsLoggingToFileEnabled() { + return g_logging_destination & LOG_TO_FILE; +} + +std::wstring GetLogFileFullPath() { + if (g_log_file_name) + return *g_log_file_name; + return std::wstring(); +} +#endif + +BASE_EXPORT void LogErrorNotReached(const char* file, int line) { + LogMessage(file, line, LOG_ERROR).stream() + << "NOTREACHED() hit."; +} + +} // namespace logging + +std::ostream& std::operator<<(std::ostream& out, const wchar_t* wstr) { + return out << (wstr ? base::WideToUTF8(wstr) : std::string()); +} diff --git a/native/zucchini/vendor/libchrome/base/logging.h b/native/zucchini/vendor/libchrome/base/logging.h new file mode 100644 index 0000000..08c1f0f --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/logging.h @@ -0,0 +1,1179 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_LOGGING_H_ +#define BASE_LOGGING_H_ + +#include + +#include +#include +#include +#include +#include +#include + +#include "base/base_export.h" +#include "base/callback_forward.h" +#include "base/compiler_specific.h" +#include "base/debug/debugger.h" +#include "base/macros.h" +#include "base/strings/string_piece_forward.h" +#include "base/template_util.h" +#include "build/build_config.h" + +// +// Optional message capabilities +// ----------------------------- +// Assertion failed messages and fatal errors are displayed in a dialog box +// before the application exits. However, running this UI creates a message +// loop, which causes application messages to be processed and potentially +// dispatched to existing application windows. Since the application is in a +// bad state when this assertion dialog is displayed, these messages may not +// get processed and hang the dialog, or the application might go crazy. +// +// Therefore, it can be beneficial to display the error dialog in a separate +// process from the main application. When the logging system needs to display +// a fatal error dialog box, it will look for a program called +// "DebugMessage.exe" in the same directory as the application executable. It +// will run this application with the message as the command line, and will +// not include the name of the application as is traditional for easier +// parsing. +// +// The code for DebugMessage.exe is only one line. In WinMain, do: +// MessageBox(NULL, GetCommandLineW(), L"Fatal Error", 0); +// +// If DebugMessage.exe is not found, the logging code will use a normal +// MessageBox, potentially causing the problems discussed above. + + +// Instructions +// ------------ +// +// Make a bunch of macros for logging. The way to log things is to stream +// things to LOG(). E.g., +// +// LOG(INFO) << "Found " << num_cookies << " cookies"; +// +// You can also do conditional logging: +// +// LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; +// +// The CHECK(condition) macro is active in both debug and release builds and +// effectively performs a LOG(FATAL) which terminates the process and +// generates a crashdump unless a debugger is attached. +// +// There are also "debug mode" logging macros like the ones above: +// +// DLOG(INFO) << "Found cookies"; +// +// DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; +// +// All "debug mode" logging is compiled away to nothing for non-debug mode +// compiles. LOG_IF and development flags also work well together +// because the code can be compiled away sometimes. +// +// We also have +// +// LOG_ASSERT(assertion); +// DLOG_ASSERT(assertion); +// +// which is syntactic sugar for {,D}LOG_IF(FATAL, assert fails) << assertion; +// +// There are "verbose level" logging macros. They look like +// +// VLOG(1) << "I'm printed when you run the program with --v=1 or more"; +// VLOG(2) << "I'm printed when you run the program with --v=2 or more"; +// +// These always log at the INFO log level (when they log at all). +// The verbose logging can also be turned on module-by-module. For instance, +// --vmodule=profile=2,icon_loader=1,browser_*=3,*/chromeos/*=4 --v=0 +// will cause: +// a. VLOG(2) and lower messages to be printed from profile.{h,cc} +// b. VLOG(1) and lower messages to be printed from icon_loader.{h,cc} +// c. VLOG(3) and lower messages to be printed from files prefixed with +// "browser" +// d. VLOG(4) and lower messages to be printed from files under a +// "chromeos" directory. +// e. VLOG(0) and lower messages to be printed from elsewhere +// +// The wildcarding functionality shown by (c) supports both '*' (match +// 0 or more characters) and '?' (match any single character) +// wildcards. Any pattern containing a forward or backward slash will +// be tested against the whole pathname and not just the module. +// E.g., "*/foo/bar/*=2" would change the logging level for all code +// in source files under a "foo/bar" directory. +// +// There's also VLOG_IS_ON(n) "verbose level" condition macro. To be used as +// +// if (VLOG_IS_ON(2)) { +// // do some logging preparation and logging +// // that can't be accomplished with just VLOG(2) << ...; +// } +// +// There is also a VLOG_IF "verbose level" condition macro for sample +// cases, when some extra computation and preparation for logs is not +// needed. +// +// VLOG_IF(1, (size > 1024)) +// << "I'm printed when size is more than 1024 and when you run the " +// "program with --v=1 or more"; +// +// We also override the standard 'assert' to use 'DLOG_ASSERT'. +// +// Lastly, there is: +// +// PLOG(ERROR) << "Couldn't do foo"; +// DPLOG(ERROR) << "Couldn't do foo"; +// PLOG_IF(ERROR, cond) << "Couldn't do foo"; +// DPLOG_IF(ERROR, cond) << "Couldn't do foo"; +// PCHECK(condition) << "Couldn't do foo"; +// DPCHECK(condition) << "Couldn't do foo"; +// +// which append the last system error to the message in string form (taken from +// GetLastError() on Windows and errno on POSIX). +// +// The supported severity levels for macros that allow you to specify one +// are (in increasing order of severity) INFO, WARNING, ERROR, and FATAL. +// +// Very important: logging a message at the FATAL severity level causes +// the program to terminate (after the message is logged). +// +// There is the special severity of DFATAL, which logs FATAL in debug mode, +// ERROR in normal mode. + +namespace logging { + +// TODO(avi): do we want to do a unification of character types here? +#if defined(OS_WIN) +typedef wchar_t PathChar; +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +typedef char PathChar; +#endif + +// Where to record logging output? A flat file and/or system debug log +// via OutputDebugString. +enum LoggingDestination { + LOG_NONE = 0, + LOG_TO_FILE = 1 << 0, + LOG_TO_SYSTEM_DEBUG_LOG = 1 << 1, + + LOG_TO_ALL = LOG_TO_FILE | LOG_TO_SYSTEM_DEBUG_LOG, + + // On Windows, use a file next to the exe; on POSIX platforms, where + // it may not even be possible to locate the executable on disk, use + // stderr. +#if defined(OS_WIN) + LOG_DEFAULT = LOG_TO_FILE, +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + LOG_DEFAULT = LOG_TO_SYSTEM_DEBUG_LOG, +#endif +}; + +// Indicates that the log file should be locked when being written to. +// Unless there is only one single-threaded process that is logging to +// the log file, the file should be locked during writes to make each +// log output atomic. Other writers will block. +// +// All processes writing to the log file must have their locking set for it to +// work properly. Defaults to LOCK_LOG_FILE. +enum LogLockingState { LOCK_LOG_FILE, DONT_LOCK_LOG_FILE }; + +// On startup, should we delete or append to an existing log file (if any)? +// Defaults to APPEND_TO_OLD_LOG_FILE. +enum OldFileDeletionState { DELETE_OLD_LOG_FILE, APPEND_TO_OLD_LOG_FILE }; + +struct BASE_EXPORT LoggingSettings { + // The defaults values are: + // + // logging_dest: LOG_DEFAULT + // log_file: NULL + // lock_log: LOCK_LOG_FILE + // delete_old: APPEND_TO_OLD_LOG_FILE + LoggingSettings(); + + LoggingDestination logging_dest; + + // The three settings below have an effect only when LOG_TO_FILE is + // set in |logging_dest|. + const PathChar* log_file; + LogLockingState lock_log; + OldFileDeletionState delete_old; +}; + +// Define different names for the BaseInitLoggingImpl() function depending on +// whether NDEBUG is defined or not so that we'll fail to link if someone tries +// to compile logging.cc with NDEBUG but includes logging.h without defining it, +// or vice versa. +#if defined(NDEBUG) +#define BaseInitLoggingImpl BaseInitLoggingImpl_built_with_NDEBUG +#else +#define BaseInitLoggingImpl BaseInitLoggingImpl_built_without_NDEBUG +#endif + +// Implementation of the InitLogging() method declared below. We use a +// more-specific name so we can #define it above without affecting other code +// that has named stuff "InitLogging". +BASE_EXPORT bool BaseInitLoggingImpl(const LoggingSettings& settings); + +// Sets the log file name and other global logging state. Calling this function +// is recommended, and is normally done at the beginning of application init. +// If you don't call it, all the flags will be initialized to their default +// values, and there is a race condition that may leak a critical section +// object if two threads try to do the first log at the same time. +// See the definition of the enums above for descriptions and default values. +// +// The default log file is initialized to "debug.log" in the application +// directory. You probably don't want this, especially since the program +// directory may not be writable on an enduser's system. +// +// This function may be called a second time to re-direct logging (e.g after +// loging in to a user partition), however it should never be called more than +// twice. +inline bool InitLogging(const LoggingSettings& settings) { + return BaseInitLoggingImpl(settings); +} + +// Sets the log level. Anything at or above this level will be written to the +// log file/displayed to the user (if applicable). Anything below this level +// will be silently ignored. The log level defaults to 0 (everything is logged +// up to level INFO) if this function is not called. +// Note that log messages for VLOG(x) are logged at level -x, so setting +// the min log level to negative values enables verbose logging. +BASE_EXPORT void SetMinLogLevel(int level); + +// Gets the current log level. +BASE_EXPORT int GetMinLogLevel(); + +// Used by LOG_IS_ON to lazy-evaluate stream arguments. +BASE_EXPORT bool ShouldCreateLogMessage(int severity); + +// Gets the VLOG default verbosity level. +BASE_EXPORT int GetVlogVerbosity(); + +// Note that |N| is the size *with* the null terminator. +BASE_EXPORT int GetVlogLevelHelper(const char* file_start, size_t N); + +// Gets the current vlog level for the given file (usually taken from __FILE__). +template +int GetVlogLevel(const char (&file)[N]) { + return GetVlogLevelHelper(file, N); +} + +// Sets the common items you want to be prepended to each log message. +// process and thread IDs default to off, the timestamp defaults to on. +// If this function is not called, logging defaults to writing the timestamp +// only. +BASE_EXPORT void SetLogItems(bool enable_process_id, bool enable_thread_id, + bool enable_timestamp, bool enable_tickcount); + +// Sets whether or not you'd like to see fatal debug messages popped up in +// a dialog box or not. +// Dialogs are not shown by default. +BASE_EXPORT void SetShowErrorDialogs(bool enable_dialogs); + +// Sets the Log Assert Handler that will be used to notify of check failures. +// Resets Log Assert Handler on object destruction. +// The default handler shows a dialog box and then terminate the process, +// however clients can use this function to override with their own handling +// (e.g. a silent one for Unit Tests) +using LogAssertHandlerFunction = + base::Callback; + +class BASE_EXPORT ScopedLogAssertHandler { + public: + explicit ScopedLogAssertHandler(LogAssertHandlerFunction handler); + ~ScopedLogAssertHandler(); + + private: + DISALLOW_COPY_AND_ASSIGN(ScopedLogAssertHandler); +}; + +// Sets the Log Message Handler that gets passed every log message before +// it's sent to other log destinations (if any). +// Returns true to signal that it handled the message and the message +// should not be sent to other log destinations. +typedef bool (*LogMessageHandlerFunction)(int severity, + const char* file, int line, size_t message_start, const std::string& str); +BASE_EXPORT void SetLogMessageHandler(LogMessageHandlerFunction handler); +BASE_EXPORT LogMessageHandlerFunction GetLogMessageHandler(); + +// The ANALYZER_ASSUME_TRUE(bool arg) macro adds compiler-specific hints +// to Clang which control what code paths are statically analyzed, +// and is meant to be used in conjunction with assert & assert-like functions. +// The expression is passed straight through if analysis isn't enabled. +// +// ANALYZER_SKIP_THIS_PATH() suppresses static analysis for the current +// codepath and any other branching codepaths that might follow. +#if defined(__clang_analyzer__) + +inline constexpr bool AnalyzerNoReturn() __attribute__((analyzer_noreturn)) { + return false; +} + +inline constexpr bool AnalyzerAssumeTrue(bool arg) { + // AnalyzerNoReturn() is invoked and analysis is terminated if |arg| is + // false. + return arg || AnalyzerNoReturn(); +} + +#define ANALYZER_ASSUME_TRUE(arg) logging::AnalyzerAssumeTrue(!!(arg)) +#define ANALYZER_SKIP_THIS_PATH() \ + static_cast(::logging::AnalyzerNoReturn()) +#define ANALYZER_ALLOW_UNUSED(var) static_cast(var); + +#else // !defined(__clang_analyzer__) + +#define ANALYZER_ASSUME_TRUE(arg) (arg) +#define ANALYZER_SKIP_THIS_PATH() +#define ANALYZER_ALLOW_UNUSED(var) static_cast(var); + +#endif // defined(__clang_analyzer__) + +typedef int LogSeverity; +const LogSeverity LOG_VERBOSE = -1; // This is level 1 verbosity +// Note: the log severities are used to index into the array of names, +// see log_severity_names. +const LogSeverity LOG_INFO = 0; +const LogSeverity LOG_WARNING = 1; +const LogSeverity LOG_ERROR = 2; +const LogSeverity LOG_FATAL = 3; +const LogSeverity LOG_NUM_SEVERITIES = 4; + +// LOG_DFATAL is LOG_FATAL in debug mode, ERROR in normal mode +#if defined(NDEBUG) +const LogSeverity LOG_DFATAL = LOG_ERROR; +#else +const LogSeverity LOG_DFATAL = LOG_FATAL; +#endif + +// A few definitions of macros that don't generate much code. These are used +// by LOG() and LOG_IF, etc. Since these are used all over our code, it's +// better to have compact code for these operations. +#define COMPACT_GOOGLE_LOG_EX_INFO(ClassName, ...) \ + ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_INFO, ##__VA_ARGS__) +#define COMPACT_GOOGLE_LOG_EX_WARNING(ClassName, ...) \ + ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_WARNING, \ + ##__VA_ARGS__) +#define COMPACT_GOOGLE_LOG_EX_ERROR(ClassName, ...) \ + ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_ERROR, ##__VA_ARGS__) +#define COMPACT_GOOGLE_LOG_EX_FATAL(ClassName, ...) \ + ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_FATAL, ##__VA_ARGS__) +#define COMPACT_GOOGLE_LOG_EX_DFATAL(ClassName, ...) \ + ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_DFATAL, ##__VA_ARGS__) +#define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \ + ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_DCHECK, ##__VA_ARGS__) + +#define COMPACT_GOOGLE_LOG_INFO COMPACT_GOOGLE_LOG_EX_INFO(LogMessage) +#define COMPACT_GOOGLE_LOG_WARNING COMPACT_GOOGLE_LOG_EX_WARNING(LogMessage) +#define COMPACT_GOOGLE_LOG_ERROR COMPACT_GOOGLE_LOG_EX_ERROR(LogMessage) +#define COMPACT_GOOGLE_LOG_FATAL COMPACT_GOOGLE_LOG_EX_FATAL(LogMessage) +#define COMPACT_GOOGLE_LOG_DFATAL COMPACT_GOOGLE_LOG_EX_DFATAL(LogMessage) +#define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_EX_DCHECK(LogMessage) + +#if defined(OS_WIN) +// wingdi.h defines ERROR to be 0. When we call LOG(ERROR), it gets +// substituted with 0, and it expands to COMPACT_GOOGLE_LOG_0. To allow us +// to keep using this syntax, we define this macro to do the same thing +// as COMPACT_GOOGLE_LOG_ERROR, and also define ERROR the same way that +// the Windows SDK does for consistency. +#define ERROR 0 +#define COMPACT_GOOGLE_LOG_EX_0(ClassName, ...) \ + COMPACT_GOOGLE_LOG_EX_ERROR(ClassName , ##__VA_ARGS__) +#define COMPACT_GOOGLE_LOG_0 COMPACT_GOOGLE_LOG_ERROR +// Needed for LOG_IS_ON(ERROR). +const LogSeverity LOG_0 = LOG_ERROR; +#endif + +// As special cases, we can assume that LOG_IS_ON(FATAL) always holds. Also, +// LOG_IS_ON(DFATAL) always holds in debug mode. In particular, CHECK()s will +// always fire if they fail. +#define LOG_IS_ON(severity) \ + (::logging::ShouldCreateLogMessage(::logging::LOG_##severity)) + +// We can't do any caching tricks with VLOG_IS_ON() like the +// google-glog version since it requires GCC extensions. This means +// that using the v-logging functions in conjunction with --vmodule +// may be slow. +#define VLOG_IS_ON(verboselevel) \ + ((verboselevel) <= ::logging::GetVlogLevel(__FILE__)) + +// Helper macro which avoids evaluating the arguments to a stream if +// the condition doesn't hold. Condition is evaluated once and only once. +#define LAZY_STREAM(stream, condition) \ + !(condition) ? (void) 0 : ::logging::LogMessageVoidify() & (stream) + +// We use the preprocessor's merging operator, "##", so that, e.g., +// LOG(INFO) becomes the token COMPACT_GOOGLE_LOG_INFO. There's some funny +// subtle difference between ostream member streaming functions (e.g., +// ostream::operator<<(int) and ostream non-member streaming functions +// (e.g., ::operator<<(ostream&, string&): it turns out that it's +// impossible to stream something like a string directly to an unnamed +// ostream. We employ a neat hack by calling the stream() member +// function of LogMessage which seems to avoid the problem. +#define LOG_STREAM(severity) COMPACT_GOOGLE_LOG_ ## severity.stream() + +#define LOG(severity) LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity)) +#define LOG_IF(severity, condition) \ + LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity) && (condition)) + +// The VLOG macros log with negative verbosities. +#define VLOG_STREAM(verbose_level) \ + ::logging::LogMessage(__FILE__, __LINE__, -verbose_level).stream() + +#define VLOG(verbose_level) \ + LAZY_STREAM(VLOG_STREAM(verbose_level), VLOG_IS_ON(verbose_level)) + +#define VLOG_IF(verbose_level, condition) \ + LAZY_STREAM(VLOG_STREAM(verbose_level), \ + VLOG_IS_ON(verbose_level) && (condition)) + +#if defined (OS_WIN) +#define VPLOG_STREAM(verbose_level) \ + ::logging::Win32ErrorLogMessage(__FILE__, __LINE__, -verbose_level, \ + ::logging::GetLastSystemErrorCode()).stream() +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +#define VPLOG_STREAM(verbose_level) \ + ::logging::ErrnoLogMessage(__FILE__, __LINE__, -verbose_level, \ + ::logging::GetLastSystemErrorCode()).stream() +#endif + +#define VPLOG(verbose_level) \ + LAZY_STREAM(VPLOG_STREAM(verbose_level), VLOG_IS_ON(verbose_level)) + +#define VPLOG_IF(verbose_level, condition) \ + LAZY_STREAM(VPLOG_STREAM(verbose_level), \ + VLOG_IS_ON(verbose_level) && (condition)) + +// TODO(akalin): Add more VLOG variants, e.g. VPLOG. + +#define LOG_ASSERT(condition) \ + LOG_IF(FATAL, !(ANALYZER_ASSUME_TRUE(condition))) \ + << "Assert failed: " #condition ". " + +#if defined(OS_WIN) +#define PLOG_STREAM(severity) \ + COMPACT_GOOGLE_LOG_EX_ ## severity(Win32ErrorLogMessage, \ + ::logging::GetLastSystemErrorCode()).stream() +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +#define PLOG_STREAM(severity) \ + COMPACT_GOOGLE_LOG_EX_ ## severity(ErrnoLogMessage, \ + ::logging::GetLastSystemErrorCode()).stream() +#endif + +#define PLOG(severity) \ + LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity)) + +#define PLOG_IF(severity, condition) \ + LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity) && (condition)) + +BASE_EXPORT extern std::ostream* g_swallow_stream; + +// Note that g_swallow_stream is used instead of an arbitrary LOG() stream to +// avoid the creation of an object with a non-trivial destructor (LogMessage). +// On MSVC x86 (checked on 2015 Update 3), this causes a few additional +// pointless instructions to be emitted even at full optimization level, even +// though the : arm of the ternary operator is clearly never executed. Using a +// simpler object to be &'d with Voidify() avoids these extra instructions. +// Using a simpler POD object with a templated operator<< also works to avoid +// these instructions. However, this causes warnings on statically defined +// implementations of operator<<(std::ostream, ...) in some .cc files, because +// they become defined-but-unreferenced functions. A reinterpret_cast of 0 to an +// ostream* also is not suitable, because some compilers warn of undefined +// behavior. +#define EAT_STREAM_PARAMETERS \ + true ? (void)0 \ + : ::logging::LogMessageVoidify() & (*::logging::g_swallow_stream) + +// Captures the result of a CHECK_EQ (for example) and facilitates testing as a +// boolean. +class CheckOpResult { + public: + // |message| must be non-null if and only if the check failed. + CheckOpResult(std::string* message) : message_(message) {} + // Returns true if the check succeeded. + operator bool() const { return !message_; } + // Returns the message. + std::string* message() { return message_; } + + private: + std::string* message_; +}; + +// Crashes in the fastest possible way with no attempt at logging. +// There are different constraints to satisfy here, see http://crbug.com/664209 +// for more context: +// - The trap instructions, and hence the PC value at crash time, have to be +// distinct and not get folded into the same opcode by the compiler. +// On Linux/Android this is tricky because GCC still folds identical +// asm volatile blocks. The workaround is generating distinct opcodes for +// each CHECK using the __COUNTER__ macro. +// - The debug info for the trap instruction has to be attributed to the source +// line that has the CHECK(), to make crash reports actionable. This rules +// out the ability of using a inline function, at least as long as clang +// doesn't support attribute(artificial). +// - Failed CHECKs should produce a signal that is distinguishable from an +// invalid memory access, to improve the actionability of crash reports. +// - The compiler should treat the CHECK as no-return instructions, so that the +// trap code can be efficiently packed in the prologue of the function and +// doesn't interfere with the main execution flow. +// - When debugging, developers shouldn't be able to accidentally step over a +// CHECK. This is achieved by putting opcodes that will cause a non +// continuable exception after the actual trap instruction. +// - Don't cause too much binary bloat. +#if defined(COMPILER_GCC) + +#if defined(ARCH_CPU_X86_FAMILY) && !defined(OS_NACL) +// int 3 will generate a SIGTRAP. +#define TRAP_SEQUENCE() \ + asm volatile( \ + "int3; ud2; push %0;" ::"i"(static_cast(__COUNTER__))) + +#elif defined(ARCH_CPU_ARMEL) && !defined(OS_NACL) +// bkpt will generate a SIGBUS when running on armv7 and a SIGTRAP when running +// as a 32 bit userspace app on arm64. There doesn't seem to be any way to +// cause a SIGTRAP from userspace without using a syscall (which would be a +// problem for sandboxing). +#define TRAP_SEQUENCE() \ + asm volatile("bkpt #0; udf %0;" ::"i"(__COUNTER__ % 256)) + +#elif defined(ARCH_CPU_ARM64) && !defined(OS_NACL) +// This will always generate a SIGTRAP on arm64. +#define TRAP_SEQUENCE() \ + asm volatile("brk #0; hlt %0;" ::"i"(__COUNTER__ % 65536)) + +#else +// Crash report accuracy will not be guaranteed on other architectures, but at +// least this will crash as expected. +#define TRAP_SEQUENCE() __builtin_trap() +#endif // ARCH_CPU_* + +// CHECK() and the trap sequence can be invoked from a constexpr function. +// This could make compilation fail on GCC, as it forbids directly using inline +// asm inside a constexpr function. However, it allows calling a lambda +// expression including the same asm. +// The side effect is that the top of the stacktrace will not point to the +// calling function, but to this anonymous lambda. This is still useful as the +// full name of the lambda will typically include the name of the function that +// calls CHECK() and the debugger will still break at the right line of code. +#if !defined(__clang__) +#define WRAPPED_TRAP_SEQUENCE() \ + do { \ + [] { TRAP_SEQUENCE(); }(); \ + } while (false) +#else +#define WRAPPED_TRAP_SEQUENCE() TRAP_SEQUENCE() +#endif + +#define IMMEDIATE_CRASH() \ + ({ \ + WRAPPED_TRAP_SEQUENCE(); \ + __builtin_unreachable(); \ + }) + +#elif defined(COMPILER_MSVC) + +// Clang is cleverer about coalescing int3s, so we need to add a unique-ish +// instruction following the __debugbreak() to have it emit distinct locations +// for CHECKs rather than collapsing them all together. It would be nice to use +// a short intrinsic to do this (and perhaps have only one implementation for +// both clang and MSVC), however clang-cl currently does not support intrinsics. +// On the flip side, MSVC x64 doesn't support inline asm. So, we have to have +// two implementations. Normally clang-cl's version will be 5 bytes (1 for +// `int3`, 2 for `ud2`, 2 for `push byte imm`, however, TODO(scottmg): +// https://crbug.com/694670 clang-cl doesn't currently support %'ing +// __COUNTER__, so eventually it will emit the dword form of push. +// TODO(scottmg): Reinvestigate a short sequence that will work on both +// compilers once clang supports more intrinsics. See https://crbug.com/693713. +#if defined(__clang__) +#define IMMEDIATE_CRASH() \ + ({ \ + {__asm int 3 __asm ud2 __asm push __COUNTER__}; \ + __builtin_unreachable(); \ + }) +#else +#define IMMEDIATE_CRASH() __debugbreak() +#endif // __clang__ + +#else +#error Port +#endif + +// CHECK dies with a fatal error if condition is not true. It is *not* +// controlled by NDEBUG, so the check will be executed regardless of +// compilation mode. +// +// We make sure CHECK et al. always evaluates their arguments, as +// doing CHECK(FunctionWithSideEffect()) is a common idiom. + +#if defined(OFFICIAL_BUILD) && defined(NDEBUG) + +// Make all CHECK functions discard their log strings to reduce code bloat, and +// improve performance, for official release builds. +// +// This is not calling BreakDebugger since this is called frequently, and +// calling an out-of-line function instead of a noreturn inline macro prevents +// compiler optimizations. +#define CHECK(condition) \ + UNLIKELY(!(condition)) ? IMMEDIATE_CRASH() : EAT_STREAM_PARAMETERS + +// PCHECK includes the system error code, which is useful for determining +// why the condition failed. In official builds, preserve only the error code +// message so that it is available in crash reports. The stringified +// condition and any additional stream parameters are dropped. +#define PCHECK(condition) \ + LAZY_STREAM(PLOG_STREAM(FATAL), UNLIKELY(!(condition))); \ + EAT_STREAM_PARAMETERS + +#define CHECK_OP(name, op, val1, val2) CHECK((val1) op (val2)) + +#else // !(OFFICIAL_BUILD && NDEBUG) + +#if defined(_PREFAST_) && defined(OS_WIN) +// Use __analysis_assume to tell the VC++ static analysis engine that +// assert conditions are true, to suppress warnings. The LAZY_STREAM +// parameter doesn't reference 'condition' in /analyze builds because +// this evaluation confuses /analyze. The !! before condition is because +// __analysis_assume gets confused on some conditions: +// http://randomascii.wordpress.com/2011/09/13/analyze-for-visual-studio-the-ugly-part-5/ + +#define CHECK(condition) \ + __analysis_assume(!!(condition)), \ + LAZY_STREAM(LOG_STREAM(FATAL), false) \ + << "Check failed: " #condition ". " + +#define PCHECK(condition) \ + __analysis_assume(!!(condition)), \ + LAZY_STREAM(PLOG_STREAM(FATAL), false) \ + << "Check failed: " #condition ". " + +#else // _PREFAST_ + +// Do as much work as possible out of line to reduce inline code size. +#define CHECK(condition) \ + LAZY_STREAM(::logging::LogMessage(__FILE__, __LINE__, #condition).stream(), \ + !ANALYZER_ASSUME_TRUE(condition)) + +#define PCHECK(condition) \ + LAZY_STREAM(PLOG_STREAM(FATAL), !ANALYZER_ASSUME_TRUE(condition)) \ + << "Check failed: " #condition ". " + +#endif // _PREFAST_ + +// Helper macro for binary operators. +// Don't use this macro directly in your code, use CHECK_EQ et al below. +// The 'switch' is used to prevent the 'else' from being ambiguous when the +// macro is used in an 'if' clause such as: +// if (a == 1) +// CHECK_EQ(2, a); +#define CHECK_OP(name, op, val1, val2) \ + switch (0) case 0: default: \ + if (::logging::CheckOpResult true_if_passed = \ + ::logging::Check##name##Impl((val1), (val2), \ + #val1 " " #op " " #val2)) \ + ; \ + else \ + ::logging::LogMessage(__FILE__, __LINE__, true_if_passed.message()).stream() + +#endif // !(OFFICIAL_BUILD && NDEBUG) + +// This formats a value for a failing CHECK_XX statement. Ordinarily, +// it uses the definition for operator<<, with a few special cases below. +template +inline typename std::enable_if< + base::internal::SupportsOstreamOperator::value && + !std::is_function::type>::value, + void>::type +MakeCheckOpValueString(std::ostream* os, const T& v) { + (*os) << v; +} + +// Provide an overload for functions and function pointers. Function pointers +// don't implicitly convert to void* but do implicitly convert to bool, so +// without this function pointers are always printed as 1 or 0. (MSVC isn't +// standards-conforming here and converts function pointers to regular +// pointers, so this is a no-op for MSVC.) +template +inline typename std::enable_if< + std::is_function::type>::value, + void>::type +MakeCheckOpValueString(std::ostream* os, const T& v) { + (*os) << reinterpret_cast(v); +} + +// We need overloads for enums that don't support operator<<. +// (i.e. scoped enums where no operator<< overload was declared). +template +inline typename std::enable_if< + !base::internal::SupportsOstreamOperator::value && + std::is_enum::value, + void>::type +MakeCheckOpValueString(std::ostream* os, const T& v) { + (*os) << static_cast::type>(v); +} + +// We need an explicit overload for std::nullptr_t. +BASE_EXPORT void MakeCheckOpValueString(std::ostream* os, std::nullptr_t p); + +// Build the error message string. This is separate from the "Impl" +// function template because it is not performance critical and so can +// be out of line, while the "Impl" code should be inline. Caller +// takes ownership of the returned string. +template +std::string* MakeCheckOpString(const t1& v1, const t2& v2, const char* names) { + std::ostringstream ss; + ss << names << " ("; + MakeCheckOpValueString(&ss, v1); + ss << " vs. "; + MakeCheckOpValueString(&ss, v2); + ss << ")"; + std::string* msg = new std::string(ss.str()); + return msg; +} + +// Commonly used instantiations of MakeCheckOpString<>. Explicitly instantiated +// in logging.cc. +extern template BASE_EXPORT std::string* MakeCheckOpString( + const int&, const int&, const char* names); +extern template BASE_EXPORT +std::string* MakeCheckOpString( + const unsigned long&, const unsigned long&, const char* names); +extern template BASE_EXPORT +std::string* MakeCheckOpString( + const unsigned long&, const unsigned int&, const char* names); +extern template BASE_EXPORT +std::string* MakeCheckOpString( + const unsigned int&, const unsigned long&, const char* names); +extern template BASE_EXPORT +std::string* MakeCheckOpString( + const std::string&, const std::string&, const char* name); + +// Helper functions for CHECK_OP macro. +// The (int, int) specialization works around the issue that the compiler +// will not instantiate the template version of the function on values of +// unnamed enum type - see comment below. +// +// The checked condition is wrapped with ANALYZER_ASSUME_TRUE, which under +// static analysis builds, blocks analysis of the current path if the +// condition is false. +#define DEFINE_CHECK_OP_IMPL(name, op) \ + template \ + inline std::string* Check##name##Impl(const t1& v1, const t2& v2, \ + const char* names) { \ + if (ANALYZER_ASSUME_TRUE(v1 op v2)) \ + return NULL; \ + else \ + return ::logging::MakeCheckOpString(v1, v2, names); \ + } \ + inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \ + if (ANALYZER_ASSUME_TRUE(v1 op v2)) \ + return NULL; \ + else \ + return ::logging::MakeCheckOpString(v1, v2, names); \ + } +DEFINE_CHECK_OP_IMPL(EQ, ==) +DEFINE_CHECK_OP_IMPL(NE, !=) +DEFINE_CHECK_OP_IMPL(LE, <=) +DEFINE_CHECK_OP_IMPL(LT, < ) +DEFINE_CHECK_OP_IMPL(GE, >=) +DEFINE_CHECK_OP_IMPL(GT, > ) +#undef DEFINE_CHECK_OP_IMPL + +#define CHECK_EQ(val1, val2) CHECK_OP(EQ, ==, val1, val2) +#define CHECK_NE(val1, val2) CHECK_OP(NE, !=, val1, val2) +#define CHECK_LE(val1, val2) CHECK_OP(LE, <=, val1, val2) +#define CHECK_LT(val1, val2) CHECK_OP(LT, < , val1, val2) +#define CHECK_GE(val1, val2) CHECK_OP(GE, >=, val1, val2) +#define CHECK_GT(val1, val2) CHECK_OP(GT, > , val1, val2) + +#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) +#define DCHECK_IS_ON() 0 +#else +#define DCHECK_IS_ON() 1 +#endif + +// Definitions for DLOG et al. + +#if DCHECK_IS_ON() + +#define DLOG_IS_ON(severity) LOG_IS_ON(severity) +#define DLOG_IF(severity, condition) LOG_IF(severity, condition) +#define DLOG_ASSERT(condition) LOG_ASSERT(condition) +#define DPLOG_IF(severity, condition) PLOG_IF(severity, condition) +#define DVLOG_IF(verboselevel, condition) VLOG_IF(verboselevel, condition) +#define DVPLOG_IF(verboselevel, condition) VPLOG_IF(verboselevel, condition) + +#else // DCHECK_IS_ON() + +// If !DCHECK_IS_ON(), we want to avoid emitting any references to |condition| +// (which may reference a variable defined only if DCHECK_IS_ON()). +// Contrast this with DCHECK et al., which has different behavior. + +#define DLOG_IS_ON(severity) false +#define DLOG_IF(severity, condition) EAT_STREAM_PARAMETERS +#define DLOG_ASSERT(condition) EAT_STREAM_PARAMETERS +#define DPLOG_IF(severity, condition) EAT_STREAM_PARAMETERS +#define DVLOG_IF(verboselevel, condition) EAT_STREAM_PARAMETERS +#define DVPLOG_IF(verboselevel, condition) EAT_STREAM_PARAMETERS + +#endif // DCHECK_IS_ON() + +#define DLOG(severity) \ + LAZY_STREAM(LOG_STREAM(severity), DLOG_IS_ON(severity)) + +#define DPLOG(severity) \ + LAZY_STREAM(PLOG_STREAM(severity), DLOG_IS_ON(severity)) + +#define DVLOG(verboselevel) DVLOG_IF(verboselevel, VLOG_IS_ON(verboselevel)) + +#define DVPLOG(verboselevel) DVPLOG_IF(verboselevel, VLOG_IS_ON(verboselevel)) + +// Definitions for DCHECK et al. + +#if DCHECK_IS_ON() + +#if DCHECK_IS_CONFIGURABLE +BASE_EXPORT extern LogSeverity LOG_DCHECK; +#else +const LogSeverity LOG_DCHECK = LOG_FATAL; +#endif + +#else // DCHECK_IS_ON() + +// There may be users of LOG_DCHECK that are enabled independently +// of DCHECK_IS_ON(), so default to FATAL logging for those. +const LogSeverity LOG_DCHECK = LOG_FATAL; + +#endif // DCHECK_IS_ON() + +// DCHECK et al. make sure to reference |condition| regardless of +// whether DCHECKs are enabled; this is so that we don't get unused +// variable warnings if the only use of a variable is in a DCHECK. +// This behavior is different from DLOG_IF et al. +// +// Note that the definition of the DCHECK macros depends on whether or not +// DCHECK_IS_ON() is true. When DCHECK_IS_ON() is false, the macros use +// EAT_STREAM_PARAMETERS to avoid expressions that would create temporaries. + +#if defined(_PREFAST_) && defined(OS_WIN) +// See comments on the previous use of __analysis_assume. + +#define DCHECK(condition) \ + __analysis_assume(!!(condition)), \ + LAZY_STREAM(LOG_STREAM(DCHECK), false) \ + << "Check failed: " #condition ". " + +#define DPCHECK(condition) \ + __analysis_assume(!!(condition)), \ + LAZY_STREAM(PLOG_STREAM(DCHECK), false) \ + << "Check failed: " #condition ". " + +#else // !(defined(_PREFAST_) && defined(OS_WIN)) + +#if DCHECK_IS_ON() + +#define DCHECK(condition) \ + LAZY_STREAM(LOG_STREAM(DCHECK), !ANALYZER_ASSUME_TRUE(condition)) \ + << "Check failed: " #condition ". " +#define DPCHECK(condition) \ + LAZY_STREAM(PLOG_STREAM(DCHECK), !ANALYZER_ASSUME_TRUE(condition)) \ + << "Check failed: " #condition ". " + +#else // DCHECK_IS_ON() + +#define DCHECK(condition) EAT_STREAM_PARAMETERS << !(condition) +#define DPCHECK(condition) EAT_STREAM_PARAMETERS << !(condition) + +#endif // DCHECK_IS_ON() + +#endif // defined(_PREFAST_) && defined(OS_WIN) + +// Helper macro for binary operators. +// Don't use this macro directly in your code, use DCHECK_EQ et al below. +// The 'switch' is used to prevent the 'else' from being ambiguous when the +// macro is used in an 'if' clause such as: +// if (a == 1) +// DCHECK_EQ(2, a); +#if DCHECK_IS_ON() + +#define DCHECK_OP(name, op, val1, val2) \ + switch (0) case 0: default: \ + if (::logging::CheckOpResult true_if_passed = \ + ::logging::Check##name##Impl((val1), (val2), \ + #val1 " " #op " " #val2)) \ + ; \ + else \ + ::logging::LogMessage(__FILE__, __LINE__, ::logging::LOG_DCHECK, \ + true_if_passed.message()).stream() + +#else // DCHECK_IS_ON() + +// When DCHECKs aren't enabled, DCHECK_OP still needs to reference operator<< +// overloads for |val1| and |val2| to avoid potential compiler warnings about +// unused functions. For the same reason, it also compares |val1| and |val2| +// using |op|. +// +// Note that the contract of DCHECK_EQ, etc is that arguments are only evaluated +// once. Even though |val1| and |val2| appear twice in this version of the macro +// expansion, this is OK, since the expression is never actually evaluated. +#define DCHECK_OP(name, op, val1, val2) \ + EAT_STREAM_PARAMETERS << (::logging::MakeCheckOpValueString( \ + ::logging::g_swallow_stream, val1), \ + ::logging::MakeCheckOpValueString( \ + ::logging::g_swallow_stream, val2), \ + (val1)op(val2)) + +#endif // DCHECK_IS_ON() + +// Equality/Inequality checks - compare two values, and log a +// LOG_DCHECK message including the two values when the result is not +// as expected. The values must have operator<<(ostream, ...) +// defined. +// +// You may append to the error message like so: +// DCHECK_NE(1, 2) << "The world must be ending!"; +// +// We are very careful to ensure that each argument is evaluated exactly +// once, and that anything which is legal to pass as a function argument is +// legal here. In particular, the arguments may be temporary expressions +// which will end up being destroyed at the end of the apparent statement, +// for example: +// DCHECK_EQ(string("abc")[1], 'b'); +// +// WARNING: These don't compile correctly if one of the arguments is a pointer +// and the other is NULL. In new code, prefer nullptr instead. To +// work around this for C++98, simply static_cast NULL to the type of the +// desired pointer. + +#define DCHECK_EQ(val1, val2) DCHECK_OP(EQ, ==, val1, val2) +#define DCHECK_NE(val1, val2) DCHECK_OP(NE, !=, val1, val2) +#define DCHECK_LE(val1, val2) DCHECK_OP(LE, <=, val1, val2) +#define DCHECK_LT(val1, val2) DCHECK_OP(LT, < , val1, val2) +#define DCHECK_GE(val1, val2) DCHECK_OP(GE, >=, val1, val2) +#define DCHECK_GT(val1, val2) DCHECK_OP(GT, > , val1, val2) + +#if !DCHECK_IS_ON() && defined(OS_CHROMEOS) +// Implement logging of NOTREACHED() as a dedicated function to get function +// call overhead down to a minimum. +void LogErrorNotReached(const char* file, int line); +#define NOTREACHED() \ + true ? ::logging::LogErrorNotReached(__FILE__, __LINE__) \ + : EAT_STREAM_PARAMETERS +#else +#define NOTREACHED() DCHECK(false) +#endif + +// Redefine the standard assert to use our nice log files +#undef assert +#define assert(x) DLOG_ASSERT(x) + +// This class more or less represents a particular log message. You +// create an instance of LogMessage and then stream stuff to it. +// When you finish streaming to it, ~LogMessage is called and the +// full message gets streamed to the appropriate destination. +// +// You shouldn't actually use LogMessage's constructor to log things, +// though. You should use the LOG() macro (and variants thereof) +// above. +class BASE_EXPORT LogMessage { + public: + // Used for LOG(severity). + LogMessage(const char* file, int line, LogSeverity severity); + + // Used for CHECK(). Implied severity = LOG_FATAL. + LogMessage(const char* file, int line, const char* condition); + + // Used for CHECK_EQ(), etc. Takes ownership of the given string. + // Implied severity = LOG_FATAL. + LogMessage(const char* file, int line, std::string* result); + + // Used for DCHECK_EQ(), etc. Takes ownership of the given string. + LogMessage(const char* file, int line, LogSeverity severity, + std::string* result); + + ~LogMessage(); + + std::ostream& stream() { return stream_; } + + LogSeverity severity() { return severity_; } + std::string str() { return stream_.str(); } + + private: + void Init(const char* file, int line); + + LogSeverity severity_; + std::ostringstream stream_; + size_t message_start_; // Offset of the start of the message (past prefix + // info). + // The file and line information passed in to the constructor. + const char* file_; + const int line_; + +#if defined(OS_WIN) + // Stores the current value of GetLastError in the constructor and restores + // it in the destructor by calling SetLastError. + // This is useful since the LogMessage class uses a lot of Win32 calls + // that will lose the value of GLE and the code that called the log function + // will have lost the thread error value when the log call returns. + class SaveLastError { + public: + SaveLastError(); + ~SaveLastError(); + + unsigned long get_error() const { return last_error_; } + + protected: + unsigned long last_error_; + }; + + SaveLastError last_error_; +#endif + + DISALLOW_COPY_AND_ASSIGN(LogMessage); +}; + +// This class is used to explicitly ignore values in the conditional +// logging macros. This avoids compiler warnings like "value computed +// is not used" and "statement has no effect". +class LogMessageVoidify { + public: + LogMessageVoidify() = default; + // This has to be an operator with a precedence lower than << but + // higher than ?: + void operator&(std::ostream&) { } +}; + +#if defined(OS_WIN) +typedef unsigned long SystemErrorCode; +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +typedef int SystemErrorCode; +#endif + +// Alias for ::GetLastError() on Windows and errno on POSIX. Avoids having to +// pull in windows.h just for GetLastError() and DWORD. +BASE_EXPORT SystemErrorCode GetLastSystemErrorCode(); +BASE_EXPORT std::string SystemErrorCodeToString(SystemErrorCode error_code); + +#if defined(OS_WIN) +// Appends a formatted system message of the GetLastError() type. +class BASE_EXPORT Win32ErrorLogMessage { + public: + Win32ErrorLogMessage(const char* file, + int line, + LogSeverity severity, + SystemErrorCode err); + + // Appends the error message before destructing the encapsulated class. + ~Win32ErrorLogMessage(); + + std::ostream& stream() { return log_message_.stream(); } + + private: + SystemErrorCode err_; + LogMessage log_message_; + + DISALLOW_COPY_AND_ASSIGN(Win32ErrorLogMessage); +}; +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +// Appends a formatted system message of the errno type +class BASE_EXPORT ErrnoLogMessage { + public: + ErrnoLogMessage(const char* file, + int line, + LogSeverity severity, + SystemErrorCode err); + + // Appends the error message before destructing the encapsulated class. + ~ErrnoLogMessage(); + + std::ostream& stream() { return log_message_.stream(); } + + private: + SystemErrorCode err_; + LogMessage log_message_; + + DISALLOW_COPY_AND_ASSIGN(ErrnoLogMessage); +}; +#endif // OS_WIN + +// Closes the log file explicitly if open. +// NOTE: Since the log file is opened as necessary by the action of logging +// statements, there's no guarantee that it will stay closed +// after this call. +BASE_EXPORT void CloseLogFile(); + +// Async signal safe logging mechanism. +BASE_EXPORT void RawLog(int level, const char* message); + +#define RAW_LOG(level, message) \ + ::logging::RawLog(::logging::LOG_##level, message) + +#define RAW_CHECK(condition) \ + do { \ + if (!(condition)) \ + ::logging::RawLog(::logging::LOG_FATAL, \ + "Check failed: " #condition "\n"); \ + } while (0) + +#if defined(OS_WIN) +// Returns true if logging to file is enabled. +BASE_EXPORT bool IsLoggingToFileEnabled(); + +// Returns the default log file path. +BASE_EXPORT std::wstring GetLogFileFullPath(); +#endif + +} // namespace logging + +// Note that "The behavior of a C++ program is undefined if it adds declarations +// or definitions to namespace std or to a namespace within namespace std unless +// otherwise specified." --C++11[namespace.std] +// +// We've checked that this particular definition has the intended behavior on +// our implementations, but it's prone to breaking in the future, and please +// don't imitate this in your own definitions without checking with some +// standard library experts. +namespace std { +// These functions are provided as a convenience for logging, which is where we +// use streams (it is against Google style to use streams in other places). It +// is designed to allow you to emit non-ASCII Unicode strings to the log file, +// which is normally ASCII. It is relatively slow, so try not to use it for +// common cases. Non-ASCII characters will be converted to UTF-8 by these +// operators. +BASE_EXPORT std::ostream& operator<<(std::ostream& out, const wchar_t* wstr); +inline std::ostream& operator<<(std::ostream& out, const std::wstring& wstr) { + return out << wstr.c_str(); +} +} // namespace std + +// The NOTIMPLEMENTED() macro annotates codepaths which have not been +// implemented yet. If output spam is a serious concern, +// NOTIMPLEMENTED_LOG_ONCE can be used. + +#if defined(COMPILER_GCC) +// On Linux, with GCC, we can use __PRETTY_FUNCTION__ to get the demangled name +// of the current function in the NOTIMPLEMENTED message. +#define NOTIMPLEMENTED_MSG "Not implemented reached in " << __PRETTY_FUNCTION__ +#else +#define NOTIMPLEMENTED_MSG "NOT IMPLEMENTED" +#endif + +#if defined(OS_ANDROID) && defined(OFFICIAL_BUILD) +#define NOTIMPLEMENTED() EAT_STREAM_PARAMETERS +#define NOTIMPLEMENTED_LOG_ONCE() EAT_STREAM_PARAMETERS +#else +#define NOTIMPLEMENTED() LOG(ERROR) << NOTIMPLEMENTED_MSG +#define NOTIMPLEMENTED_LOG_ONCE() \ + do { \ + static bool logged_once = false; \ + LOG_IF(ERROR, !logged_once) << NOTIMPLEMENTED_MSG; \ + logged_once = true; \ + } while (0); \ + EAT_STREAM_PARAMETERS +#endif + +#endif // BASE_LOGGING_H_ diff --git a/native/zucchini/vendor/libchrome/base/macros.h b/native/zucchini/vendor/libchrome/base/macros.h new file mode 100644 index 0000000..8685117 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/macros.h @@ -0,0 +1,114 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file contains macros and macro-like constructs (e.g., templates) that +// are commonly used throughout Chromium source. (It may also contain things +// that are closely related to things that are commonly used that belong in this +// file.) + +#ifndef BASE_MACROS_H_ +#define BASE_MACROS_H_ + +#include // For size_t. + +#if defined(ANDROID) +// Prefer Android's libbase definitions to our own. +#include +#endif // defined(ANDROID) + +// We define following macros conditionally as they may be defined by another libraries. + +// Distinguish mips32. +#if defined(__mips__) && (_MIPS_SIM == _ABIO32) && !defined(__mips32__) +#define __mips32__ +#endif + +// Distinguish mips64. +#if defined(__mips__) && (_MIPS_SIM == _ABI64) && !defined(__mips64__) +#define __mips64__ +#endif + +// Put this in the declarations for a class to be uncopyable. +#if !defined(DISALLOW_COPY) +#define DISALLOW_COPY(TypeName) \ + TypeName(const TypeName&) = delete +#endif + +// Put this in the declarations for a class to be unassignable. +#if !defined(DISALLOW_ASSIGN) +#define DISALLOW_ASSIGN(TypeName) TypeName& operator=(const TypeName&) = delete +#endif + +// Put this in the declarations for a class to be uncopyable and unassignable. +#if !defined(DISALLOW_COPY_AND_ASSIGN) +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + DISALLOW_COPY(TypeName); \ + DISALLOW_ASSIGN(TypeName) +#endif + +// A macro to disallow all the implicit constructors, namely the +// default constructor, copy constructor and operator= functions. +// This is especially useful for classes containing only static methods. +#if !defined(DISALLOW_IMPLICIT_CONSTRUCTORS) +#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ + TypeName() = delete; \ + DISALLOW_COPY_AND_ASSIGN(TypeName) +#endif + +// The arraysize(arr) macro returns the # of elements in an array arr. The +// expression is a compile-time constant, and therefore can be used in defining +// new arrays, for example. If you use arraysize on a pointer by mistake, you +// will get a compile-time error. For the technical details, refer to +// http://blogs.msdn.com/b/the1/archive/2004/05/07/128242.aspx. + +// This template function declaration is used in defining arraysize. +// Note that the function doesn't need an implementation, as we only +// use its type. +// +// DEPRECATED, please use base::size(array) instead. +// TODO(https://crbug.com/837308): Replace existing arraysize usages. +#if !defined(arraysize) +template char (&ArraySizeHelper(T (&array)[N]))[N]; +#define arraysize(array) (sizeof(ArraySizeHelper(array))) +#endif + +// Used to explicitly mark the return value of a function as unused. If you are +// really sure you don't want to do anything with the return value of a function +// that has been marked WARN_UNUSED_RESULT, wrap it with this. Example: +// +// std::unique_ptr my_var = ...; +// if (TakeOwnership(my_var.get()) == SUCCESS) +// ignore_result(my_var.release()); +// +template +inline void ignore_result(const T&) { +} + +namespace base { + +// Use these to declare and define a static local variable (static T;) so that +// it is leaked so that its destructors are not called at exit. This is +// thread-safe. +// +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DEPRECATED !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// Please don't use this macro. Use a function-local static of type +// base::NoDestructor instead: +// +// Factory& Factory::GetInstance() { +// static base::NoDestructor instance; +// return *instance; +// } +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +#if !defined(CR_DEFINE_STATIC_LOCAL) +#define CR_DEFINE_STATIC_LOCAL(type, name, arguments) \ + static type& name = *new type arguments +#endif + +// Workaround for MSVC, which expands __VA_ARGS__ as one macro argument. To +// work around this bug, wrap the entire expression in this macro... +#define CR_EXPAND_ARG(arg) arg + +} // base + +#endif // BASE_MACROS_H_ diff --git a/native/zucchini/vendor/libchrome/base/memory/free_deleter.h b/native/zucchini/vendor/libchrome/base/memory/free_deleter.h new file mode 100644 index 0000000..5604118 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/memory/free_deleter.h @@ -0,0 +1,25 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MEMORY_FREE_DELETER_H_ +#define BASE_MEMORY_FREE_DELETER_H_ + +#include + +namespace base { + +// Function object which invokes 'free' on its parameter, which must be +// a pointer. Can be used to store malloc-allocated pointers in std::unique_ptr: +// +// std::unique_ptr foo_ptr( +// static_cast(malloc(sizeof(int)))); +struct FreeDeleter { + inline void operator()(void* ptr) const { + free(ptr); + } +}; + +} // namespace base + +#endif // BASE_MEMORY_FREE_DELETER_H_ diff --git a/native/zucchini/vendor/libchrome/base/memory/ptr_util.h b/native/zucchini/vendor/libchrome/base/memory/ptr_util.h new file mode 100644 index 0000000..42f4f49 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/memory/ptr_util.h @@ -0,0 +1,23 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MEMORY_PTR_UTIL_H_ +#define BASE_MEMORY_PTR_UTIL_H_ + +#include +#include + +namespace base { + +// Helper to transfer ownership of a raw pointer to a std::unique_ptr. +// Note that std::unique_ptr has very different semantics from +// std::unique_ptr: do not use this helper for array allocations. +template +std::unique_ptr WrapUnique(T* ptr) { + return std::unique_ptr(ptr); +} + +} // namespace base + +#endif // BASE_MEMORY_PTR_UTIL_H_ diff --git a/native/zucchini/vendor/libchrome/base/memory/raw_scoped_refptr_mismatch_checker.h b/native/zucchini/vendor/libchrome/base/memory/raw_scoped_refptr_mismatch_checker.h new file mode 100644 index 0000000..ab8b2ab --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/memory/raw_scoped_refptr_mismatch_checker.h @@ -0,0 +1,52 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MEMORY_RAW_SCOPED_REFPTR_MISMATCH_CHECKER_H_ +#define BASE_MEMORY_RAW_SCOPED_REFPTR_MISMATCH_CHECKER_H_ + +#include + +#include "base/template_util.h" + +// It is dangerous to post a task with a T* argument where T is a subtype of +// RefCounted(Base|ThreadSafeBase), since by the time the parameter is used, the +// object may already have been deleted since it was not held with a +// scoped_refptr. Example: http://crbug.com/27191 +// The following set of traits are designed to generate a compile error +// whenever this antipattern is attempted. + +namespace base { + +// This is a base internal implementation file used by task.h and callback.h. +// Not for public consumption, so we wrap it in namespace internal. +namespace internal { + +template +struct IsRefCountedType : std::false_type {}; + +template +struct IsRefCountedType()->AddRef()), + decltype(std::declval()->Release())>> + : std::true_type {}; + +template +struct NeedsScopedRefptrButGetsRawPtr { + static_assert(!std::is_reference::value, + "NeedsScopedRefptrButGetsRawPtr requires non-reference type."); + + enum { + // Human readable translation: you needed to be a scoped_refptr if you are a + // raw pointer type and are convertible to a RefCounted(Base|ThreadSafeBase) + // type. + value = std::is_pointer::value && + IsRefCountedType>::value + }; +}; + +} // namespace internal + +} // namespace base + +#endif // BASE_MEMORY_RAW_SCOPED_REFPTR_MISMATCH_CHECKER_H_ diff --git a/native/zucchini/vendor/libchrome/base/memory/ref_counted.h b/native/zucchini/vendor/libchrome/base/memory/ref_counted.h new file mode 100644 index 0000000..249f70e --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/memory/ref_counted.h @@ -0,0 +1,425 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MEMORY_REF_COUNTED_H_ +#define BASE_MEMORY_REF_COUNTED_H_ + +#include + +#include + +#include "base/atomic_ref_count.h" +#include "base/base_export.h" +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/memory/scoped_refptr.h" +#include "base/sequence_checker.h" +#include "base/threading/thread_collision_warner.h" +#include "build/build_config.h" + +namespace base { +namespace subtle { + +class BASE_EXPORT RefCountedBase { + public: + bool HasOneRef() const { return ref_count_ == 1; } + + protected: + explicit RefCountedBase(StartRefCountFromZeroTag) { +#if DCHECK_IS_ON() + sequence_checker_.DetachFromSequence(); +#endif + } + + explicit RefCountedBase(StartRefCountFromOneTag) : ref_count_(1) { +#if DCHECK_IS_ON() + needs_adopt_ref_ = true; + sequence_checker_.DetachFromSequence(); +#endif + } + + ~RefCountedBase() { +#if DCHECK_IS_ON() + DCHECK(in_dtor_) << "RefCounted object deleted without calling Release()"; +#endif + } + + void AddRef() const { + // TODO(maruel): Add back once it doesn't assert 500 times/sec. + // Current thread books the critical section "AddRelease" + // without release it. + // DFAKE_SCOPED_LOCK_THREAD_LOCKED(add_release_); +#if DCHECK_IS_ON() + DCHECK(!in_dtor_); + DCHECK(!needs_adopt_ref_) + << "This RefCounted object is created with non-zero reference count." + << " The first reference to such a object has to be made by AdoptRef or" + << " MakeRefCounted."; + if (ref_count_ >= 1) { + DCHECK(CalledOnValidSequence()); + } +#endif + + AddRefImpl(); + } + + // Returns true if the object should self-delete. + bool Release() const { + --ref_count_; + + // TODO(maruel): Add back once it doesn't assert 500 times/sec. + // Current thread books the critical section "AddRelease" + // without release it. + // DFAKE_SCOPED_LOCK_THREAD_LOCKED(add_release_); + +#if DCHECK_IS_ON() + DCHECK(!in_dtor_); + if (ref_count_ == 0) + in_dtor_ = true; + + if (ref_count_ >= 1) + DCHECK(CalledOnValidSequence()); + if (ref_count_ == 1) + sequence_checker_.DetachFromSequence(); +#endif + + return ref_count_ == 0; + } + + // Returns true if it is safe to read or write the object, from a thread + // safety standpoint. Should be DCHECK'd from the methods of RefCounted + // classes if there is a danger of objects being shared across threads. + // + // This produces fewer false positives than adding a separate SequenceChecker + // into the subclass, because it automatically detaches from the sequence when + // the reference count is 1 (and never fails if there is only one reference). + // + // This means unlike a separate SequenceChecker, it will permit a singly + // referenced object to be passed between threads (not holding a reference on + // the sending thread), but will trap if the sending thread holds onto a + // reference, or if the object is accessed from multiple threads + // simultaneously. + bool IsOnValidSequence() const { +#if DCHECK_IS_ON() + return ref_count_ <= 1 || CalledOnValidSequence(); +#else + return true; +#endif + } + + private: + template + friend scoped_refptr base::AdoptRef(U*); + + void Adopted() const { +#if DCHECK_IS_ON() + DCHECK(needs_adopt_ref_); + needs_adopt_ref_ = false; +#endif + } + +#if defined(ARCH_CPU_64_BIT) + void AddRefImpl() const; +#else + void AddRefImpl() const { ++ref_count_; } +#endif + +#if DCHECK_IS_ON() + bool CalledOnValidSequence() const; +#endif + + mutable uint32_t ref_count_ = 0; + +#if DCHECK_IS_ON() + mutable bool needs_adopt_ref_ = false; + mutable bool in_dtor_ = false; + mutable SequenceChecker sequence_checker_; +#endif + + DFAKE_MUTEX(add_release_); + + DISALLOW_COPY_AND_ASSIGN(RefCountedBase); +}; + +class BASE_EXPORT RefCountedThreadSafeBase { + public: + bool HasOneRef() const; + + protected: + explicit constexpr RefCountedThreadSafeBase(StartRefCountFromZeroTag) {} + explicit constexpr RefCountedThreadSafeBase(StartRefCountFromOneTag) + : ref_count_(1) { +#if DCHECK_IS_ON() + needs_adopt_ref_ = true; +#endif + } + +#if DCHECK_IS_ON() + ~RefCountedThreadSafeBase(); +#else + ~RefCountedThreadSafeBase() = default; +#endif + +// Release and AddRef are suitable for inlining on X86 because they generate +// very small code sequences. On other platforms (ARM), it causes a size +// regression and is probably not worth it. +#if defined(ARCH_CPU_X86_FAMILY) + // Returns true if the object should self-delete. + bool Release() const { return ReleaseImpl(); } + void AddRef() const { AddRefImpl(); } +#else + // Returns true if the object should self-delete. + bool Release() const; + void AddRef() const; +#endif + + private: + template + friend scoped_refptr base::AdoptRef(U*); + + void Adopted() const { +#if DCHECK_IS_ON() + DCHECK(needs_adopt_ref_); + needs_adopt_ref_ = false; +#endif + } + + ALWAYS_INLINE void AddRefImpl() const { +#if DCHECK_IS_ON() + DCHECK(!in_dtor_); + DCHECK(!needs_adopt_ref_) + << "This RefCounted object is created with non-zero reference count." + << " The first reference to such a object has to be made by AdoptRef or" + << " MakeRefCounted."; +#endif + ref_count_.Increment(); + } + + ALWAYS_INLINE bool ReleaseImpl() const { +#if DCHECK_IS_ON() + DCHECK(!in_dtor_); + DCHECK(!ref_count_.IsZero()); +#endif + if (!ref_count_.Decrement()) { +#if DCHECK_IS_ON() + in_dtor_ = true; +#endif + return true; + } + return false; + } + + mutable AtomicRefCount ref_count_{0}; +#if DCHECK_IS_ON() + mutable bool needs_adopt_ref_ = false; + mutable bool in_dtor_ = false; +#endif + + DISALLOW_COPY_AND_ASSIGN(RefCountedThreadSafeBase); +}; + +} // namespace subtle + +// ScopedAllowCrossThreadRefCountAccess disables the check documented on +// RefCounted below for rare pre-existing use cases where thread-safety was +// guaranteed through other means (e.g. explicit sequencing of calls across +// execution sequences when bouncing between threads in order). New callers +// should refrain from using this (callsites handling thread-safety through +// locks should use RefCountedThreadSafe per the overhead of its atomics being +// negligible compared to locks anyways and callsites doing explicit sequencing +// should properly std::move() the ref to avoid hitting this check). +// TODO(tzik): Cleanup existing use cases and remove +// ScopedAllowCrossThreadRefCountAccess. +class BASE_EXPORT ScopedAllowCrossThreadRefCountAccess final { + public: +#if DCHECK_IS_ON() + ScopedAllowCrossThreadRefCountAccess(); + ~ScopedAllowCrossThreadRefCountAccess(); +#else + ScopedAllowCrossThreadRefCountAccess() {} + ~ScopedAllowCrossThreadRefCountAccess() {} +#endif +}; + +// +// A base class for reference counted classes. Otherwise, known as a cheap +// knock-off of WebKit's RefCounted class. To use this, just extend your +// class from it like so: +// +// class MyFoo : public base::RefCounted { +// ... +// private: +// friend class base::RefCounted; +// ~MyFoo(); +// }; +// +// You should always make your destructor non-public, to avoid any code deleting +// the object accidently while there are references to it. +// +// +// The ref count manipulation to RefCounted is NOT thread safe and has DCHECKs +// to trap unsafe cross thread usage. A subclass instance of RefCounted can be +// passed to another execution sequence only when its ref count is 1. If the ref +// count is more than 1, the RefCounted class verifies the ref updates are made +// on the same execution sequence as the previous ones. The subclass can also +// manually call IsOnValidSequence to trap other non-thread-safe accesses; see +// the documentation for that method. +// +// +// The reference count starts from zero by default, and we intended to migrate +// to start-from-one ref count. Put REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE() to +// the ref counted class to opt-in. +// +// If an object has start-from-one ref count, the first scoped_refptr need to be +// created by base::AdoptRef() or base::MakeRefCounted(). We can use +// base::MakeRefCounted() to create create both type of ref counted object. +// +// The motivations to use start-from-one ref count are: +// - Start-from-one ref count doesn't need the ref count increment for the +// first reference. +// - It can detect an invalid object acquisition for a being-deleted object +// that has zero ref count. That tends to happen on custom deleter that +// delays the deletion. +// TODO(tzik): Implement invalid acquisition detection. +// - Behavior parity to Blink's WTF::RefCounted, whose count starts from one. +// And start-from-one ref count is a step to merge WTF::RefCounted into +// base::RefCounted. +// +#define REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE() \ + static constexpr ::base::subtle::StartRefCountFromOneTag \ + kRefCountPreference = ::base::subtle::kStartRefCountFromOneTag + +template +class RefCounted; + +template +struct DefaultRefCountedTraits { + static void Destruct(const T* x) { + RefCounted::DeleteInternal(x); + } +}; + +template > +class RefCounted : public subtle::RefCountedBase { + public: + static constexpr subtle::StartRefCountFromZeroTag kRefCountPreference = + subtle::kStartRefCountFromZeroTag; + + RefCounted() : subtle::RefCountedBase(T::kRefCountPreference) {} + + void AddRef() const { + subtle::RefCountedBase::AddRef(); + } + + void Release() const { + if (subtle::RefCountedBase::Release()) { + // Prune the code paths which the static analyzer may take to simulate + // object destruction. Use-after-free errors aren't possible given the + // lifetime guarantees of the refcounting system. + ANALYZER_SKIP_THIS_PATH(); + + Traits::Destruct(static_cast(this)); + } + } + + protected: + ~RefCounted() = default; + + private: + friend struct DefaultRefCountedTraits; + template + static void DeleteInternal(const U* x) { + delete x; + } + + DISALLOW_COPY_AND_ASSIGN(RefCounted); +}; + +// Forward declaration. +template class RefCountedThreadSafe; + +// Default traits for RefCountedThreadSafe. Deletes the object when its ref +// count reaches 0. Overload to delete it on a different thread etc. +template +struct DefaultRefCountedThreadSafeTraits { + static void Destruct(const T* x) { + // Delete through RefCountedThreadSafe to make child classes only need to be + // friend with RefCountedThreadSafe instead of this struct, which is an + // implementation detail. + RefCountedThreadSafe::DeleteInternal(x); + } +}; + +// +// A thread-safe variant of RefCounted +// +// class MyFoo : public base::RefCountedThreadSafe { +// ... +// }; +// +// If you're using the default trait, then you should add compile time +// asserts that no one else is deleting your object. i.e. +// private: +// friend class base::RefCountedThreadSafe; +// ~MyFoo(); +// +// We can use REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE() with RefCountedThreadSafe +// too. See the comment above the RefCounted definition for details. +template > +class RefCountedThreadSafe : public subtle::RefCountedThreadSafeBase { + public: + static constexpr subtle::StartRefCountFromZeroTag kRefCountPreference = + subtle::kStartRefCountFromZeroTag; + + explicit RefCountedThreadSafe() + : subtle::RefCountedThreadSafeBase(T::kRefCountPreference) {} + + void AddRef() const { + subtle::RefCountedThreadSafeBase::AddRef(); + } + + void Release() const { + if (subtle::RefCountedThreadSafeBase::Release()) { + ANALYZER_SKIP_THIS_PATH(); + Traits::Destruct(static_cast(this)); + } + } + + protected: + ~RefCountedThreadSafe() = default; + + private: + friend struct DefaultRefCountedThreadSafeTraits; + template + static void DeleteInternal(const U* x) { + delete x; + } + + DISALLOW_COPY_AND_ASSIGN(RefCountedThreadSafe); +}; + +// +// A thread-safe wrapper for some piece of data so we can place other +// things in scoped_refptrs<>. +// +template +class RefCountedData + : public base::RefCountedThreadSafe< base::RefCountedData > { + public: + RefCountedData() : data() {} + RefCountedData(const T& in_value) : data(in_value) {} + RefCountedData(T&& in_value) : data(std::move(in_value)) {} + + T data; + + private: + friend class base::RefCountedThreadSafe >; + ~RefCountedData() = default; +}; + +} // namespace base + +#endif // BASE_MEMORY_REF_COUNTED_H_ diff --git a/native/zucchini/vendor/libchrome/base/memory/scoped_refptr.h b/native/zucchini/vendor/libchrome/base/memory/scoped_refptr.h new file mode 100644 index 0000000..389d0cb --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/memory/scoped_refptr.h @@ -0,0 +1,337 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MEMORY_SCOPED_REFPTR_H_ +#define BASE_MEMORY_SCOPED_REFPTR_H_ + +#include + +#include +#include +#include + +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/macros.h" + +template +class scoped_refptr; + +namespace base { + +template +class RefCounted; +template +class RefCountedThreadSafe; + +template +scoped_refptr AdoptRef(T* t); + +namespace subtle { + +enum AdoptRefTag { kAdoptRefTag }; +enum StartRefCountFromZeroTag { kStartRefCountFromZeroTag }; +enum StartRefCountFromOneTag { kStartRefCountFromOneTag }; + +template +constexpr bool IsRefCountPreferenceOverridden(const T*, + const RefCounted*) { + return !std::is_same, + std::decay_t>::value; +} + +template +constexpr bool IsRefCountPreferenceOverridden( + const T*, + const RefCountedThreadSafe*) { + return !std::is_same, + std::decay_t>::value; +} + +constexpr bool IsRefCountPreferenceOverridden(...) { + return false; +} + +} // namespace subtle + +// Creates a scoped_refptr from a raw pointer without incrementing the reference +// count. Use this only for a newly created object whose reference count starts +// from 1 instead of 0. +template +scoped_refptr AdoptRef(T* obj) { + using Tag = std::decay_t; + static_assert(std::is_same::value, + "Use AdoptRef only for the reference count starts from one."); + + DCHECK(obj); + DCHECK(obj->HasOneRef()); + obj->Adopted(); + return scoped_refptr(obj, subtle::kAdoptRefTag); +} + +namespace subtle { + +template +scoped_refptr AdoptRefIfNeeded(T* obj, StartRefCountFromZeroTag) { + return scoped_refptr(obj); +} + +template +scoped_refptr AdoptRefIfNeeded(T* obj, StartRefCountFromOneTag) { + return AdoptRef(obj); +} + +} // namespace subtle + +// Constructs an instance of T, which is a ref counted type, and wraps the +// object into a scoped_refptr. +template +scoped_refptr MakeRefCounted(Args&&... args) { + T* obj = new T(std::forward(args)...); + return subtle::AdoptRefIfNeeded(obj, T::kRefCountPreference); +} + +// Takes an instance of T, which is a ref counted type, and wraps the object +// into a scoped_refptr. +template +scoped_refptr WrapRefCounted(T* t) { + return scoped_refptr(t); +} + +} // namespace base + +// +// A smart pointer class for reference counted objects. Use this class instead +// of calling AddRef and Release manually on a reference counted object to +// avoid common memory leaks caused by forgetting to Release an object +// reference. Sample usage: +// +// class MyFoo : public RefCounted { +// ... +// private: +// friend class RefCounted; // Allow destruction by RefCounted<>. +// ~MyFoo(); // Destructor must be private/protected. +// }; +// +// void some_function() { +// scoped_refptr foo = MakeRefCounted(); +// foo->Method(param); +// // |foo| is released when this function returns +// } +// +// void some_other_function() { +// scoped_refptr foo = MakeRefCounted(); +// ... +// foo.reset(); // explicitly releases |foo| +// ... +// if (foo) +// foo->Method(param); +// } +// +// The above examples show how scoped_refptr acts like a pointer to T. +// Given two scoped_refptr classes, it is also possible to exchange +// references between the two objects, like so: +// +// { +// scoped_refptr a = MakeRefCounted(); +// scoped_refptr b; +// +// b.swap(a); +// // now, |b| references the MyFoo object, and |a| references nullptr. +// } +// +// To make both |a| and |b| in the above example reference the same MyFoo +// object, simply use the assignment operator: +// +// { +// scoped_refptr a = MakeRefCounted(); +// scoped_refptr b; +// +// b = a; +// // now, |a| and |b| each own a reference to the same MyFoo object. +// } +// +// Also see Chromium's ownership and calling conventions: +// https://chromium.googlesource.com/chromium/src/+/lkgr/styleguide/c++/c++.md#object-ownership-and-calling-conventions +// Specifically: +// If the function (at least sometimes) takes a ref on a refcounted object, +// declare the param as scoped_refptr. The caller can decide whether it +// wishes to transfer ownership (by calling std::move(t) when passing t) or +// retain its ref (by simply passing t directly). +// In other words, use scoped_refptr like you would a std::unique_ptr except +// in the odd case where it's required to hold on to a ref while handing one +// to another component (if a component merely needs to use t on the stack +// without keeping a ref: pass t as a raw T*). +template +class scoped_refptr { + public: + typedef T element_type; + + constexpr scoped_refptr() = default; + + // Constructs from raw pointer. constexpr if |p| is null. + constexpr scoped_refptr(T* p) : ptr_(p) { + if (ptr_) + AddRef(ptr_); + } + + // Copy constructor. This is required in addition to the copy conversion + // constructor below. + scoped_refptr(const scoped_refptr& r) : scoped_refptr(r.ptr_) {} + + // Copy conversion constructor. + template ::value>::type> + scoped_refptr(const scoped_refptr& r) : scoped_refptr(r.ptr_) {} + + // Move constructor. This is required in addition to the move conversion + // constructor below. + scoped_refptr(scoped_refptr&& r) noexcept : ptr_(r.ptr_) { r.ptr_ = nullptr; } + + // Move conversion constructor. + template ::value>::type> + scoped_refptr(scoped_refptr&& r) noexcept : ptr_(r.ptr_) { + r.ptr_ = nullptr; + } + + ~scoped_refptr() { + static_assert(!base::subtle::IsRefCountPreferenceOverridden( + static_cast(nullptr), static_cast(nullptr)), + "It's unsafe to override the ref count preference." + " Please remove REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE" + " from subclasses."); + if (ptr_) + Release(ptr_); + } + + T* get() const { return ptr_; } + + T& operator*() const { + DCHECK(ptr_); + return *ptr_; + } + + T* operator->() const { + DCHECK(ptr_); + return ptr_; + } + + scoped_refptr& operator=(T* p) { return *this = scoped_refptr(p); } + + // Unified assignment operator. + scoped_refptr& operator=(scoped_refptr r) noexcept { + swap(r); + return *this; + } + + // Sets managed object to null and releases reference to the previous managed + // object, if it existed. + void reset() { scoped_refptr().swap(*this); } + + void swap(scoped_refptr& r) noexcept { std::swap(ptr_, r.ptr_); } + + explicit operator bool() const { return ptr_ != nullptr; } + + template + bool operator==(const scoped_refptr& rhs) const { + return ptr_ == rhs.get(); + } + + template + bool operator!=(const scoped_refptr& rhs) const { + return !operator==(rhs); + } + + template + bool operator<(const scoped_refptr& rhs) const { + return ptr_ < rhs.get(); + } + + protected: + T* ptr_ = nullptr; + + private: + template + friend scoped_refptr base::AdoptRef(U*); + + scoped_refptr(T* p, base::subtle::AdoptRefTag) : ptr_(p) {} + + // Friend required for move constructors that set r.ptr_ to null. + template + friend class scoped_refptr; + + // Non-inline helpers to allow: + // class Opaque; + // extern template class scoped_refptr; + // Otherwise the compiler will complain that Opaque is an incomplete type. + static void AddRef(T* ptr); + static void Release(T* ptr); +}; + +// static +template +void scoped_refptr::AddRef(T* ptr) { + ptr->AddRef(); +} + +// static +template +void scoped_refptr::Release(T* ptr) { + ptr->Release(); +} + +template +bool operator==(const scoped_refptr& lhs, const U* rhs) { + return lhs.get() == rhs; +} + +template +bool operator==(const T* lhs, const scoped_refptr& rhs) { + return lhs == rhs.get(); +} + +template +bool operator==(const scoped_refptr& lhs, std::nullptr_t null) { + return !static_cast(lhs); +} + +template +bool operator==(std::nullptr_t null, const scoped_refptr& rhs) { + return !static_cast(rhs); +} + +template +bool operator!=(const scoped_refptr& lhs, const U* rhs) { + return !operator==(lhs, rhs); +} + +template +bool operator!=(const T* lhs, const scoped_refptr& rhs) { + return !operator==(lhs, rhs); +} + +template +bool operator!=(const scoped_refptr& lhs, std::nullptr_t null) { + return !operator==(lhs, null); +} + +template +bool operator!=(std::nullptr_t null, const scoped_refptr& rhs) { + return !operator==(null, rhs); +} + +template +std::ostream& operator<<(std::ostream& out, const scoped_refptr& p) { + return out << p.get(); +} + +template +void swap(scoped_refptr& lhs, scoped_refptr& rhs) noexcept { + lhs.swap(rhs); +} + +#endif // BASE_MEMORY_SCOPED_REFPTR_H_ diff --git a/native/zucchini/vendor/libchrome/base/memory/shared_memory.h b/native/zucchini/vendor/libchrome/base/memory/shared_memory.h new file mode 100644 index 0000000..c573ef7 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/memory/shared_memory.h @@ -0,0 +1,254 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MEMORY_SHARED_MEMORY_H_ +#define BASE_MEMORY_SHARED_MEMORY_H_ + +#include + +#include + +#include "base/base_export.h" +#include "base/hash.h" +#include "base/macros.h" +#include "base/memory/shared_memory_handle.h" +#include "base/process/process_handle.h" +#include "base/strings/string16.h" +#include "build/build_config.h" + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) +#include +#include +#include +#include "base/file_descriptor_posix.h" +#include "base/files/file_util.h" +#include "base/files/scoped_file.h" +#endif + +#if defined(OS_WIN) +#include "base/win/scoped_handle.h" +#endif + +namespace base { + +class FilePath; + +// Options for creating a shared memory object. +struct BASE_EXPORT SharedMemoryCreateOptions { +#if defined(OS_MACOSX) && !defined(OS_IOS) + // The type of OS primitive that should back the SharedMemory object. + SharedMemoryHandle::Type type = SharedMemoryHandle::MACH; +#elif !defined(OS_FUCHSIA) + // DEPRECATED (crbug.com/345734): + // If NULL, the object is anonymous. This pointer is owned by the caller + // and must live through the call to Create(). + const std::string* name_deprecated = nullptr; + + // DEPRECATED (crbug.com/345734): + // If true, and the shared memory already exists, Create() will open the + // existing shared memory and ignore the size parameter. If false, + // shared memory must not exist. This flag is meaningless unless + // name_deprecated is non-NULL. + bool open_existing_deprecated = false; +#endif // defined(OS_MACOSX) && !defined(OS_IOS) + + // Size of the shared memory object to be created. + // When opening an existing object, this has no effect. + size_t size = 0; + + // If true, mappings might need to be made executable later. + bool executable = false; + + // If true, the file can be shared read-only to a process. + bool share_read_only = false; +}; + +// Platform abstraction for shared memory. +// SharedMemory consumes a SharedMemoryHandle [potentially one that it created] +// to map a shared memory OS resource into the virtual address space of the +// current process. +class BASE_EXPORT SharedMemory { + public: + SharedMemory(); + +#if defined(OS_WIN) + // Similar to the default constructor, except that this allows for + // calling LockDeprecated() to acquire the named mutex before either Create or + // Open are called on Windows. + explicit SharedMemory(const string16& name); +#endif + + // Create a new SharedMemory object from an existing, open + // shared memory file. + // + // WARNING: This does not reduce the OS-level permissions on the handle; it + // only affects how the SharedMemory will be mmapped. Use + // GetReadOnlyHandle to drop permissions. TODO(jln,jyasskin): DCHECK + // that |read_only| matches the permissions of the handle. + SharedMemory(const SharedMemoryHandle& handle, bool read_only); + + // Closes any open files. + ~SharedMemory(); + + // Return true iff the given handle is valid (i.e. not the distingished + // invalid value; NULL for a HANDLE and -1 for a file descriptor) + static bool IsHandleValid(const SharedMemoryHandle& handle); + + // Closes a shared memory handle. + static void CloseHandle(const SharedMemoryHandle& handle); + + // Returns the maximum number of handles that can be open at once per process. + static size_t GetHandleLimit(); + + // Duplicates The underlying OS primitive. Returns an invalid handle on + // failure. The caller is responsible for destroying the duplicated OS + // primitive. + static SharedMemoryHandle DuplicateHandle(const SharedMemoryHandle& handle); + +#if defined(OS_POSIX) + // This method requires that the SharedMemoryHandle is backed by a POSIX fd. + static int GetFdFromSharedMemoryHandle(const SharedMemoryHandle& handle); +#endif + + // Creates a shared memory object as described by the options struct. + // Returns true on success and false on failure. + bool Create(const SharedMemoryCreateOptions& options); + + // Creates and maps an anonymous shared memory segment of size size. + // Returns true on success and false on failure. + bool CreateAndMapAnonymous(size_t size); + + // Creates an anonymous shared memory segment of size size. + // Returns true on success and false on failure. + bool CreateAnonymous(size_t size) { + SharedMemoryCreateOptions options; + options.size = size; + return Create(options); + } + +#if (!defined(OS_MACOSX) || defined(OS_IOS)) && !defined(OS_FUCHSIA) + // DEPRECATED (crbug.com/345734): + // Creates or opens a shared memory segment based on a name. + // If open_existing is true, and the shared memory already exists, + // opens the existing shared memory and ignores the size parameter. + // If open_existing is false, shared memory must not exist. + // size is the size of the block to be created. + // Returns true on success, false on failure. + bool CreateNamedDeprecated( + const std::string& name, bool open_existing, size_t size) { + SharedMemoryCreateOptions options; + options.name_deprecated = &name; + options.open_existing_deprecated = open_existing; + options.size = size; + return Create(options); + } + + // Deletes resources associated with a shared memory segment based on name. + // Not all platforms require this call. + bool Delete(const std::string& name); + + // Opens a shared memory segment based on a name. + // If read_only is true, opens for read-only access. + // Returns true on success, false on failure. + bool Open(const std::string& name, bool read_only); +#endif // !defined(OS_MACOSX) || defined(OS_IOS) + + // Maps the shared memory into the caller's address space. + // Returns true on success, false otherwise. The memory address + // is accessed via the memory() accessor. The mapped address is guaranteed to + // have an alignment of at least MAP_MINIMUM_ALIGNMENT. This method will fail + // if this object is currently mapped. + bool Map(size_t bytes) { + return MapAt(0, bytes); + } + + // Same as above, but with |offset| to specify from begining of the shared + // memory block to map. + // |offset| must be alignent to value of |SysInfo::VMAllocationGranularity()|. + bool MapAt(off_t offset, size_t bytes); + enum { MAP_MINIMUM_ALIGNMENT = 32 }; + + // Unmaps the shared memory from the caller's address space. + // Returns true if successful; returns false on error or if the + // memory is not mapped. + bool Unmap(); + + // The size requested when the map is first created. + size_t requested_size() const { return requested_size_; } + + // The actual size of the mapped memory (may be larger than requested). + size_t mapped_size() const { return mapped_size_; } + + // Gets a pointer to the opened memory space if it has been + // Mapped via Map(). Returns NULL if it is not mapped. + void* memory() const { return memory_; } + + // Returns the underlying OS handle for this segment. + // Use of this handle for anything other than an opaque + // identifier is not portable. + SharedMemoryHandle handle() const; + + // Returns the underlying OS handle for this segment. The caller takes + // ownership of the handle and memory is unmapped. This is equivalent to + // duplicating the handle and then calling Unmap() and Close() on this object, + // without the overhead of duplicating the handle. + SharedMemoryHandle TakeHandle(); + + // Closes the open shared memory segment. The memory will remain mapped if + // it was previously mapped. + // It is safe to call Close repeatedly. + void Close(); + + // Returns a read-only handle to this shared memory region. The caller takes + // ownership of the handle. For POSIX handles, CHECK-fails if the region + // wasn't Created or Opened with share_read_only=true, which is required to + // make the handle read-only. When the handle is passed to the IPC subsystem, + // that takes ownership of the handle. As such, it's not valid to pass the + // sample handle to the IPC subsystem twice. Returns an invalid handle on + // failure. + SharedMemoryHandle GetReadOnlyHandle() const; + + // Returns an ID for the mapped region. This is ID of the SharedMemoryHandle + // that was mapped. The ID is valid even after the SharedMemoryHandle is + // Closed, as long as the region is not unmapped. + const UnguessableToken& mapped_id() const { return mapped_id_; } + + private: +#if defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_ANDROID) && \ + (!defined(OS_MACOSX) || defined(OS_IOS)) + bool FilePathForMemoryName(const std::string& mem_name, FilePath* path); +#endif + +#if defined(OS_WIN) + // If true indicates this came from an external source so needs extra checks + // before being mapped. + bool external_section_ = false; + string16 name_; +#elif !defined(OS_ANDROID) && !defined(OS_FUCHSIA) + // If valid, points to the same memory region as shm_, but with readonly + // permissions. + SharedMemoryHandle readonly_shm_; +#endif + +#if defined(OS_MACOSX) && !defined(OS_IOS) + // The mechanism by which the memory is mapped. Only valid if |memory_| is not + // |nullptr|. + SharedMemoryHandle::Type mapped_memory_mechanism_ = SharedMemoryHandle::MACH; +#endif + + // The OS primitive that backs the shared memory region. + SharedMemoryHandle shm_; + + size_t mapped_size_ = 0; + void* memory_ = nullptr; + bool read_only_ = false; + size_t requested_size_ = 0; + base::UnguessableToken mapped_id_; + + DISALLOW_COPY_AND_ASSIGN(SharedMemory); +}; + +} // namespace base + +#endif // BASE_MEMORY_SHARED_MEMORY_H_ diff --git a/native/zucchini/vendor/libchrome/base/memory/shared_memory_handle.h b/native/zucchini/vendor/libchrome/base/memory/shared_memory_handle.h new file mode 100644 index 0000000..dd3d47a --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/memory/shared_memory_handle.h @@ -0,0 +1,238 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MEMORY_SHARED_MEMORY_HANDLE_H_ +#define BASE_MEMORY_SHARED_MEMORY_HANDLE_H_ + +#include + +#include "base/unguessable_token.h" +#include "build/build_config.h" + +#if defined(OS_WIN) +#include "base/process/process_handle.h" +#include "base/win/windows_types.h" +#elif defined(OS_MACOSX) && !defined(OS_IOS) +#include +#include "base/base_export.h" +#include "base/file_descriptor_posix.h" +#include "base/macros.h" +#include "base/process/process_handle.h" +#elif defined(OS_POSIX) +#include +#include "base/file_descriptor_posix.h" +#elif defined(OS_FUCHSIA) +#include +#endif + +namespace base { + +// SharedMemoryHandle is the smallest possible IPC-transportable "reference" to +// a shared memory OS resource. A "reference" can be consumed exactly once [by +// base::SharedMemory] to map the shared memory OS resource into the virtual +// address space of the current process. +// TODO(erikchen): This class should have strong ownership semantics to prevent +// leaks of the underlying OS resource. https://crbug.com/640840. +class BASE_EXPORT SharedMemoryHandle { + public: + // The default constructor returns an invalid SharedMemoryHandle. + SharedMemoryHandle(); + + // Standard copy constructor. The new instance shares the underlying OS + // primitives. + SharedMemoryHandle(const SharedMemoryHandle& handle); + + // Standard assignment operator. The updated instance shares the underlying + // OS primitives. + SharedMemoryHandle& operator=(const SharedMemoryHandle& handle); + + // Closes the underlying OS resource. + // The fact that this method needs to be "const" is an artifact of the + // original interface for base::SharedMemory::CloseHandle. + // TODO(erikchen): This doesn't clear the underlying reference, which seems + // like a bug, but is how this class has always worked. Fix this: + // https://crbug.com/716072. + void Close() const; + + // Whether ownership of the underlying OS resource is implicitly passed to + // the IPC subsystem during serialization. + void SetOwnershipPassesToIPC(bool ownership_passes); + bool OwnershipPassesToIPC() const; + + // Whether the underlying OS resource is valid. + bool IsValid() const; + + // Duplicates the underlying OS resource. Using the return value as a + // parameter to an IPC message will cause the IPC subsystem to consume the OS + // resource. + SharedMemoryHandle Duplicate() const; + + // Uniques identifies the shared memory region that the underlying OS resource + // points to. Multiple SharedMemoryHandles that point to the same shared + // memory region will have the same GUID. Preserved across IPC. + base::UnguessableToken GetGUID() const; + + // Returns the size of the memory region that SharedMemoryHandle points to. + size_t GetSize() const; + +#if defined(OS_WIN) + // Takes implicit ownership of |h|. + // |guid| uniquely identifies the shared memory region pointed to by the + // underlying OS resource. If the HANDLE is associated with another + // SharedMemoryHandle, the caller must pass the |guid| of that + // SharedMemoryHandle. Otherwise, the caller should generate a new + // UnguessableToken. + // Passing the wrong |size| has no immediate consequence, but may cause errors + // when trying to map the SharedMemoryHandle at a later point in time. + SharedMemoryHandle(HANDLE h, size_t size, const base::UnguessableToken& guid); + HANDLE GetHandle() const; +#elif defined(OS_FUCHSIA) + // Takes implicit ownership of |h|. + // |guid| uniquely identifies the shared memory region pointed to by the + // underlying OS resource. If the zx_handle_t is associated with another + // SharedMemoryHandle, the caller must pass the |guid| of that + // SharedMemoryHandle. Otherwise, the caller should generate a new + // UnguessableToken. + // Passing the wrong |size| has no immediate consequence, but may cause errors + // when trying to map the SharedMemoryHandle at a later point in time. + SharedMemoryHandle(zx_handle_t h, + size_t size, + const base::UnguessableToken& guid); + zx_handle_t GetHandle() const; +#elif defined(OS_MACOSX) && !defined(OS_IOS) + enum Type { + // The SharedMemoryHandle is backed by a POSIX fd. + POSIX, + // The SharedMemoryHandle is backed by the Mach primitive "memory object". + MACH, + }; + + // Makes a Mach-based SharedMemoryHandle of the given size. On error, + // subsequent calls to IsValid() return false. + // Passing the wrong |size| has no immediate consequence, but may cause errors + // when trying to map the SharedMemoryHandle at a later point in time. + SharedMemoryHandle(mach_vm_size_t size, const base::UnguessableToken& guid); + + // Makes a Mach-based SharedMemoryHandle from |memory_object|, a named entry + // in the current task. The memory region has size |size|. + // Passing the wrong |size| has no immediate consequence, but may cause errors + // when trying to map the SharedMemoryHandle at a later point in time. + SharedMemoryHandle(mach_port_t memory_object, + mach_vm_size_t size, + const base::UnguessableToken& guid); + + // Exposed so that the SharedMemoryHandle can be transported between + // processes. + mach_port_t GetMemoryObject() const; + + // The SharedMemoryHandle must be valid. + // Returns whether the SharedMemoryHandle was successfully mapped into memory. + // On success, |memory| is an output variable that contains the start of the + // mapped memory. + bool MapAt(off_t offset, size_t bytes, void** memory, bool read_only); +#elif defined(OS_POSIX) + // Creates a SharedMemoryHandle from an |fd| supplied from an external + // service. + // Passing the wrong |size| has no immediate consequence, but may cause errors + // when trying to map the SharedMemoryHandle at a later point in time. + static SharedMemoryHandle ImportHandle(int fd, size_t size); + + // Returns the underlying OS resource. + int GetHandle() const; + + // Invalidates [but doesn't close] the underlying OS resource. This will leak + // unless the caller is careful. + int Release(); +#endif + +#if defined(OS_ANDROID) + // Marks the current file descriptor as read-only, for the purpose of + // mapping. This is independent of the region's read-only status. + void SetReadOnly() { read_only_ = true; } + + // Returns true iff the descriptor is to be used for read-only + // mappings. + bool IsReadOnly() const { return read_only_; } + + // Returns true iff the corresponding region is read-only. + bool IsRegionReadOnly() const; + + // Try to set the region read-only. This will fail any future attempt + // at read-write mapping. + bool SetRegionReadOnly() const; +#endif + +#if defined(OS_POSIX) + // Constructs a SharedMemoryHandle backed by a FileDescriptor. The newly + // created instance has the same ownership semantics as base::FileDescriptor. + // This typically means that the SharedMemoryHandle takes ownership of the + // |fd| if |auto_close| is true. Unfortunately, it's common for existing code + // to make shallow copies of SharedMemoryHandle, and the one that is finally + // passed into a base::SharedMemory is the one that "consumes" the fd. + // + // |guid| uniquely identifies the shared memory region pointed to by the + // underlying OS resource. If |file_descriptor| is associated with another + // SharedMemoryHandle, the caller must pass the |guid| of that + // SharedMemoryHandle. Otherwise, the caller should generate a new + // UnguessableToken. + // Passing the wrong |size| has no immediate consequence, but may cause errors + // when trying to map the SharedMemoryHandle at a later point in time. + SharedMemoryHandle(const base::FileDescriptor& file_descriptor, + size_t size, + const base::UnguessableToken& guid); +#endif + + private: +#if defined(OS_WIN) + HANDLE handle_ = nullptr; + + // Whether passing this object as a parameter to an IPC message passes + // ownership of |handle_| to the IPC stack. This is meant to mimic the + // behavior of the |auto_close| parameter of FileDescriptor. This member only + // affects attachment-brokered SharedMemoryHandles. + // Defaults to |false|. + bool ownership_passes_to_ipc_ = false; +#elif defined(OS_FUCHSIA) + zx_handle_t handle_ = ZX_HANDLE_INVALID; + bool ownership_passes_to_ipc_ = false; +#elif defined(OS_MACOSX) && !defined(OS_IOS) + friend class SharedMemory; + friend bool CheckReadOnlySharedMemoryHandleForTesting( + SharedMemoryHandle handle); + + Type type_ = MACH; + + // Each instance of a SharedMemoryHandle is backed either by a POSIX fd or a + // mach port. |type_| determines the backing member. + union { + FileDescriptor file_descriptor_; + + struct { + mach_port_t memory_object_ = MACH_PORT_NULL; + + // Whether passing this object as a parameter to an IPC message passes + // ownership of |memory_object_| to the IPC stack. This is meant to mimic + // the behavior of the |auto_close| parameter of FileDescriptor. + // Defaults to |false|. + bool ownership_passes_to_ipc_ = false; + }; + }; +#elif defined(OS_ANDROID) + friend class SharedMemory; + + FileDescriptor file_descriptor_; + bool read_only_ = false; +#elif defined(OS_POSIX) + FileDescriptor file_descriptor_; +#endif + + base::UnguessableToken guid_; + + // The size of the region referenced by the SharedMemoryHandle. + size_t size_ = 0; +}; + +} // namespace base + +#endif // BASE_MEMORY_SHARED_MEMORY_HANDLE_H_ diff --git a/native/zucchini/vendor/libchrome/base/memory/singleton.h b/native/zucchini/vendor/libchrome/base/memory/singleton.h new file mode 100644 index 0000000..880ef0a --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/memory/singleton.h @@ -0,0 +1,262 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// PLEASE READ: Do you really need a singleton? If possible, use a +// function-local static of type base::NoDestructor instead: +// +// Factory& Factory::GetInstance() { +// static base::NoDestructor instance; +// return *instance; +// } +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// +// Singletons make it hard to determine the lifetime of an object, which can +// lead to buggy code and spurious crashes. +// +// Instead of adding another singleton into the mix, try to identify either: +// a) An existing singleton that can manage your object's lifetime +// b) Locations where you can deterministically create the object and pass +// into other objects +// +// If you absolutely need a singleton, please keep them as trivial as possible +// and ideally a leaf dependency. Singletons get problematic when they attempt +// to do too much in their destructor or have circular dependencies. + +#ifndef BASE_MEMORY_SINGLETON_H_ +#define BASE_MEMORY_SINGLETON_H_ + +#include "base/at_exit.h" +#include "base/atomicops.h" +#include "base/base_export.h" +#include "base/lazy_instance_helpers.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/threading/thread_restrictions.h" + +namespace base { + +// Default traits for Singleton. Calls operator new and operator delete on +// the object. Registers automatic deletion at process exit. +// Overload if you need arguments or another memory allocation function. +template +struct DefaultSingletonTraits { + // Allocates the object. + static Type* New() { + // The parenthesis is very important here; it forces POD type + // initialization. + return new Type(); + } + + // Destroys the object. + static void Delete(Type* x) { + delete x; + } + + // Set to true to automatically register deletion of the object on process + // exit. See below for the required call that makes this happen. + static const bool kRegisterAtExit = true; + +#if DCHECK_IS_ON() + // Set to false to disallow access on a non-joinable thread. This is + // different from kRegisterAtExit because StaticMemorySingletonTraits allows + // access on non-joinable threads, and gracefully handles this. + static const bool kAllowedToAccessOnNonjoinableThread = false; +#endif +}; + + +// Alternate traits for use with the Singleton. Identical to +// DefaultSingletonTraits except that the Singleton will not be cleaned up +// at exit. +template +struct LeakySingletonTraits : public DefaultSingletonTraits { + static const bool kRegisterAtExit = false; +#if DCHECK_IS_ON() + static const bool kAllowedToAccessOnNonjoinableThread = true; +#endif +}; + +// Alternate traits for use with the Singleton. Allocates memory +// for the singleton instance from a static buffer. The singleton will +// be cleaned up at exit, but can't be revived after destruction unless +// the ResurrectForTesting() method is called. +// +// This is useful for a certain category of things, notably logging and +// tracing, where the singleton instance is of a type carefully constructed to +// be safe to access post-destruction. +// In logging and tracing you'll typically get stray calls at odd times, like +// during static destruction, thread teardown and the like, and there's a +// termination race on the heap-based singleton - e.g. if one thread calls +// get(), but then another thread initiates AtExit processing, the first thread +// may call into an object residing in unallocated memory. If the instance is +// allocated from the data segment, then this is survivable. +// +// The destructor is to deallocate system resources, in this case to unregister +// a callback the system will invoke when logging levels change. Note that +// this is also used in e.g. Chrome Frame, where you have to allow for the +// possibility of loading briefly into someone else's process space, and +// so leaking is not an option, as that would sabotage the state of your host +// process once you've unloaded. +template +struct StaticMemorySingletonTraits { + // WARNING: User has to support a New() which returns null. + static Type* New() { + // Only constructs once and returns pointer; otherwise returns null. + if (subtle::NoBarrier_AtomicExchange(&dead_, 1)) + return nullptr; + + return new (buffer_) Type(); + } + + static void Delete(Type* p) { + if (p) + p->Type::~Type(); + } + + static const bool kRegisterAtExit = true; + +#if DCHECK_IS_ON() + static const bool kAllowedToAccessOnNonjoinableThread = true; +#endif + + static void ResurrectForTesting() { subtle::NoBarrier_Store(&dead_, 0); } + + private: + alignas(Type) static char buffer_[sizeof(Type)]; + // Signal the object was already deleted, so it is not revived. + static subtle::Atomic32 dead_; +}; + +template +alignas(Type) char StaticMemorySingletonTraits::buffer_[sizeof(Type)]; +template +subtle::Atomic32 StaticMemorySingletonTraits::dead_ = 0; + +// The Singleton class manages a single +// instance of Type which will be created on first use and will be destroyed at +// normal process exit). The Trait::Delete function will not be called on +// abnormal process exit. +// +// DifferentiatingType is used as a key to differentiate two different +// singletons having the same memory allocation functions but serving a +// different purpose. This is mainly used for Locks serving different purposes. +// +// Example usage: +// +// In your header: +// namespace base { +// template +// struct DefaultSingletonTraits; +// } +// class FooClass { +// public: +// static FooClass* GetInstance(); <-- See comment below on this. +// void Bar() { ... } +// private: +// FooClass() { ... } +// friend struct base::DefaultSingletonTraits; +// +// DISALLOW_COPY_AND_ASSIGN(FooClass); +// }; +// +// In your source file: +// #include "base/memory/singleton.h" +// FooClass* FooClass::GetInstance() { +// return base::Singleton::get(); +// } +// +// Or for leaky singletons: +// #include "base/memory/singleton.h" +// FooClass* FooClass::GetInstance() { +// return base::Singleton< +// FooClass, base::LeakySingletonTraits>::get(); +// } +// +// And to call methods on FooClass: +// FooClass::GetInstance()->Bar(); +// +// NOTE: The method accessing Singleton::get() has to be named as GetInstance +// and it is important that FooClass::GetInstance() is not inlined in the +// header. This makes sure that when source files from multiple targets include +// this header they don't end up with different copies of the inlined code +// creating multiple copies of the singleton. +// +// Singleton<> has no non-static members and doesn't need to actually be +// instantiated. +// +// This class is itself thread-safe. The underlying Type must of course be +// thread-safe if you want to use it concurrently. Two parameters may be tuned +// depending on the user's requirements. +// +// Glossary: +// RAE = kRegisterAtExit +// +// On every platform, if Traits::RAE is true, the singleton will be destroyed at +// process exit. More precisely it uses AtExitManager which requires an +// object of this type to be instantiated. AtExitManager mimics the semantics +// of atexit() such as LIFO order but under Windows is safer to call. For more +// information see at_exit.h. +// +// If Traits::RAE is false, the singleton will not be freed at process exit, +// thus the singleton will be leaked if it is ever accessed. Traits::RAE +// shouldn't be false unless absolutely necessary. Remember that the heap where +// the object is allocated may be destroyed by the CRT anyway. +// +// Caveats: +// (a) Every call to get(), operator->() and operator*() incurs some overhead +// (16ns on my P4/2.8GHz) to check whether the object has already been +// initialized. You may wish to cache the result of get(); it will not +// change. +// +// (b) Your factory function must never throw an exception. This class is not +// exception-safe. +// + +template , + typename DifferentiatingType = Type> +class Singleton { + private: + // Classes using the Singleton pattern should declare a GetInstance() + // method and call Singleton::get() from within that. + friend Type* Type::GetInstance(); + + // This class is safe to be constructed and copy-constructed since it has no + // member. + + // Return a pointer to the one true instance of the class. + static Type* get() { +#if DCHECK_IS_ON() + if (!Traits::kAllowedToAccessOnNonjoinableThread) + ThreadRestrictions::AssertSingletonAllowed(); +#endif + + return subtle::GetOrCreateLazyPointer( + &instance_, &CreatorFunc, nullptr, + Traits::kRegisterAtExit ? OnExit : nullptr, nullptr); + } + + // Internal method used as an adaptor for GetOrCreateLazyPointer(). Do not use + // outside of that use case. + static Type* CreatorFunc(void* /* creator_arg*/) { return Traits::New(); } + + // Adapter function for use with AtExit(). This should be called single + // threaded, so don't use atomic operations. + // Calling OnExit while singleton is in use by other threads is a mistake. + static void OnExit(void* /*unused*/) { + // AtExit should only ever be register after the singleton instance was + // created. We should only ever get here with a valid instance_ pointer. + Traits::Delete(reinterpret_cast(subtle::NoBarrier_Load(&instance_))); + instance_ = 0; + } + static subtle::AtomicWord instance_; +}; + +template +subtle::AtomicWord Singleton::instance_ = 0; + +} // namespace base + +#endif // BASE_MEMORY_SINGLETON_H_ diff --git a/native/zucchini/vendor/libchrome/base/memory/weak_ptr.h b/native/zucchini/vendor/libchrome/base/memory/weak_ptr.h new file mode 100644 index 0000000..34e7d2e --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/memory/weak_ptr.h @@ -0,0 +1,377 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Weak pointers are pointers to an object that do not affect its lifetime, +// and which may be invalidated (i.e. reset to nullptr) by the object, or its +// owner, at any time, most commonly when the object is about to be deleted. + +// Weak pointers are useful when an object needs to be accessed safely by one +// or more objects other than its owner, and those callers can cope with the +// object vanishing and e.g. tasks posted to it being silently dropped. +// Reference-counting such an object would complicate the ownership graph and +// make it harder to reason about the object's lifetime. + +// EXAMPLE: +// +// class Controller { +// public: +// Controller() : weak_factory_(this) {} +// void SpawnWorker() { Worker::StartNew(weak_factory_.GetWeakPtr()); } +// void WorkComplete(const Result& result) { ... } +// private: +// // Member variables should appear before the WeakPtrFactory, to ensure +// // that any WeakPtrs to Controller are invalidated before its members +// // variable's destructors are executed, rendering them invalid. +// WeakPtrFactory weak_factory_; +// }; +// +// class Worker { +// public: +// static void StartNew(const WeakPtr& controller) { +// Worker* worker = new Worker(controller); +// // Kick off asynchronous processing... +// } +// private: +// Worker(const WeakPtr& controller) +// : controller_(controller) {} +// void DidCompleteAsynchronousProcessing(const Result& result) { +// if (controller_) +// controller_->WorkComplete(result); +// } +// WeakPtr controller_; +// }; +// +// With this implementation a caller may use SpawnWorker() to dispatch multiple +// Workers and subsequently delete the Controller, without waiting for all +// Workers to have completed. + +// ------------------------- IMPORTANT: Thread-safety ------------------------- + +// Weak pointers may be passed safely between threads, but must always be +// dereferenced and invalidated on the same SequencedTaskRunner otherwise +// checking the pointer would be racey. +// +// To ensure correct use, the first time a WeakPtr issued by a WeakPtrFactory +// is dereferenced, the factory and its WeakPtrs become bound to the calling +// thread or current SequencedWorkerPool token, and cannot be dereferenced or +// invalidated on any other task runner. Bound WeakPtrs can still be handed +// off to other task runners, e.g. to use to post tasks back to object on the +// bound sequence. +// +// If all WeakPtr objects are destroyed or invalidated then the factory is +// unbound from the SequencedTaskRunner/Thread. The WeakPtrFactory may then be +// destroyed, or new WeakPtr objects may be used, from a different sequence. +// +// Thus, at least one WeakPtr object must exist and have been dereferenced on +// the correct thread to enforce that other WeakPtr objects will enforce they +// are used on the desired thread. + +#ifndef BASE_MEMORY_WEAK_PTR_H_ +#define BASE_MEMORY_WEAK_PTR_H_ + +#include +#include + +#include "base/base_export.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/sequence_checker.h" + +namespace base { + +template class SupportsWeakPtr; +template class WeakPtr; + +namespace internal { +// These classes are part of the WeakPtr implementation. +// DO NOT USE THESE CLASSES DIRECTLY YOURSELF. + +class BASE_EXPORT WeakReference { + public: + // Although Flag is bound to a specific SequencedTaskRunner, it may be + // deleted from another via base::WeakPtr::~WeakPtr(). + class BASE_EXPORT Flag : public RefCountedThreadSafe { + public: + Flag(); + + void Invalidate(); + bool IsValid() const; + + private: + friend class base::RefCountedThreadSafe; + + ~Flag(); + + SequenceChecker sequence_checker_; + bool is_valid_; + }; + + WeakReference(); + explicit WeakReference(const scoped_refptr& flag); + ~WeakReference(); + + WeakReference(WeakReference&& other); + WeakReference(const WeakReference& other); + WeakReference& operator=(WeakReference&& other) = default; + WeakReference& operator=(const WeakReference& other) = default; + + bool is_valid() const; + + private: + scoped_refptr flag_; +}; + +class BASE_EXPORT WeakReferenceOwner { + public: + WeakReferenceOwner(); + ~WeakReferenceOwner(); + + WeakReference GetRef() const; + + bool HasRefs() const { return flag_ && !flag_->HasOneRef(); } + + void Invalidate(); + + private: + mutable scoped_refptr flag_; +}; + +// This class simplifies the implementation of WeakPtr's type conversion +// constructor by avoiding the need for a public accessor for ref_. A +// WeakPtr cannot access the private members of WeakPtr, so this +// base class gives us a way to access ref_ in a protected fashion. +class BASE_EXPORT WeakPtrBase { + public: + WeakPtrBase(); + ~WeakPtrBase(); + + WeakPtrBase(const WeakPtrBase& other) = default; + WeakPtrBase(WeakPtrBase&& other) = default; + WeakPtrBase& operator=(const WeakPtrBase& other) = default; + WeakPtrBase& operator=(WeakPtrBase&& other) = default; + + void reset() { + ref_ = internal::WeakReference(); + ptr_ = 0; + } + + protected: + WeakPtrBase(const WeakReference& ref, uintptr_t ptr); + + WeakReference ref_; + + // This pointer is only valid when ref_.is_valid() is true. Otherwise, its + // value is undefined (as opposed to nullptr). + uintptr_t ptr_; +}; + +// This class provides a common implementation of common functions that would +// otherwise get instantiated separately for each distinct instantiation of +// SupportsWeakPtr<>. +class SupportsWeakPtrBase { + public: + // A safe static downcast of a WeakPtr to WeakPtr. This + // conversion will only compile if there is exists a Base which inherits + // from SupportsWeakPtr. See base::AsWeakPtr() below for a helper + // function that makes calling this easier. + // + // Precondition: t != nullptr + template + static WeakPtr StaticAsWeakPtr(Derived* t) { + static_assert( + std::is_base_of::value, + "AsWeakPtr argument must inherit from SupportsWeakPtr"); + return AsWeakPtrImpl(t); + } + + private: + // This template function uses type inference to find a Base of Derived + // which is an instance of SupportsWeakPtr. We can then safely + // static_cast the Base* to a Derived*. + template + static WeakPtr AsWeakPtrImpl(SupportsWeakPtr* t) { + WeakPtr ptr = t->AsWeakPtr(); + return WeakPtr( + ptr.ref_, static_cast(reinterpret_cast(ptr.ptr_))); + } +}; + +} // namespace internal + +template class WeakPtrFactory; + +// The WeakPtr class holds a weak reference to |T*|. +// +// This class is designed to be used like a normal pointer. You should always +// null-test an object of this class before using it or invoking a method that +// may result in the underlying object being destroyed. +// +// EXAMPLE: +// +// class Foo { ... }; +// WeakPtr foo; +// if (foo) +// foo->method(); +// +template +class WeakPtr : public internal::WeakPtrBase { + public: + WeakPtr() = default; + + WeakPtr(std::nullptr_t) {} + + // Allow conversion from U to T provided U "is a" T. Note that this + // is separate from the (implicit) copy and move constructors. + template + WeakPtr(const WeakPtr& other) : WeakPtrBase(other) { + // Need to cast from U* to T* to do pointer adjustment in case of multiple + // inheritance. This also enforces the "U is a T" rule. + T* t = reinterpret_cast(other.ptr_); + ptr_ = reinterpret_cast(t); + } + template + WeakPtr(WeakPtr&& other) : WeakPtrBase(std::move(other)) { + // Need to cast from U* to T* to do pointer adjustment in case of multiple + // inheritance. This also enforces the "U is a T" rule. + T* t = reinterpret_cast(other.ptr_); + ptr_ = reinterpret_cast(t); + } + + T* get() const { + return ref_.is_valid() ? reinterpret_cast(ptr_) : nullptr; + } + + T& operator*() const { + DCHECK(get() != nullptr); + return *get(); + } + T* operator->() const { + DCHECK(get() != nullptr); + return get(); + } + + // Allow conditionals to test validity, e.g. if (weak_ptr) {...}; + explicit operator bool() const { return get() != nullptr; } + + private: + friend class internal::SupportsWeakPtrBase; + template friend class WeakPtr; + friend class SupportsWeakPtr; + friend class WeakPtrFactory; + + WeakPtr(const internal::WeakReference& ref, T* ptr) + : WeakPtrBase(ref, reinterpret_cast(ptr)) {} +}; + +// Allow callers to compare WeakPtrs against nullptr to test validity. +template +bool operator!=(const WeakPtr& weak_ptr, std::nullptr_t) { + return !(weak_ptr == nullptr); +} +template +bool operator!=(std::nullptr_t, const WeakPtr& weak_ptr) { + return weak_ptr != nullptr; +} +template +bool operator==(const WeakPtr& weak_ptr, std::nullptr_t) { + return weak_ptr.get() == nullptr; +} +template +bool operator==(std::nullptr_t, const WeakPtr& weak_ptr) { + return weak_ptr == nullptr; +} + +namespace internal { +class BASE_EXPORT WeakPtrFactoryBase { + protected: + WeakPtrFactoryBase(uintptr_t ptr); + ~WeakPtrFactoryBase(); + internal::WeakReferenceOwner weak_reference_owner_; + uintptr_t ptr_; +}; +} // namespace internal + +// A class may be composed of a WeakPtrFactory and thereby +// control how it exposes weak pointers to itself. This is helpful if you only +// need weak pointers within the implementation of a class. This class is also +// useful when working with primitive types. For example, you could have a +// WeakPtrFactory that is used to pass around a weak reference to a bool. +template +class WeakPtrFactory : public internal::WeakPtrFactoryBase { + public: + explicit WeakPtrFactory(T* ptr) + : WeakPtrFactoryBase(reinterpret_cast(ptr)) {} + + ~WeakPtrFactory() = default; + + WeakPtr GetWeakPtr() { + return WeakPtr(weak_reference_owner_.GetRef(), + reinterpret_cast(ptr_)); + } + + // Call this method to invalidate all existing weak pointers. + void InvalidateWeakPtrs() { + DCHECK(ptr_); + weak_reference_owner_.Invalidate(); + } + + // Call this method to determine if any weak pointers exist. + bool HasWeakPtrs() const { + DCHECK(ptr_); + return weak_reference_owner_.HasRefs(); + } + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(WeakPtrFactory); +}; + +// A class may extend from SupportsWeakPtr to let others take weak pointers to +// it. This avoids the class itself implementing boilerplate to dispense weak +// pointers. However, since SupportsWeakPtr's destructor won't invalidate +// weak pointers to the class until after the derived class' members have been +// destroyed, its use can lead to subtle use-after-destroy issues. +template +class SupportsWeakPtr : public internal::SupportsWeakPtrBase { + public: + SupportsWeakPtr() = default; + + WeakPtr AsWeakPtr() { + return WeakPtr(weak_reference_owner_.GetRef(), static_cast(this)); + } + + protected: + ~SupportsWeakPtr() = default; + + private: + internal::WeakReferenceOwner weak_reference_owner_; + DISALLOW_COPY_AND_ASSIGN(SupportsWeakPtr); +}; + +// Helper function that uses type deduction to safely return a WeakPtr +// when Derived doesn't directly extend SupportsWeakPtr, instead it +// extends a Base that extends SupportsWeakPtr. +// +// EXAMPLE: +// class Base : public base::SupportsWeakPtr {}; +// class Derived : public Base {}; +// +// Derived derived; +// base::WeakPtr ptr = base::AsWeakPtr(&derived); +// +// Note that the following doesn't work (invalid type conversion) since +// Derived::AsWeakPtr() is WeakPtr SupportsWeakPtr::AsWeakPtr(), +// and there's no way to safely cast WeakPtr to WeakPtr at +// the caller. +// +// base::WeakPtr ptr = derived.AsWeakPtr(); // Fails. + +template +WeakPtr AsWeakPtr(Derived* t) { + return internal::SupportsWeakPtrBase::StaticAsWeakPtr(t); +} + +} // namespace base + +#endif // BASE_MEMORY_WEAK_PTR_H_ diff --git a/native/zucchini/vendor/libchrome/base/metrics/bucket_ranges.h b/native/zucchini/vendor/libchrome/base/metrics/bucket_ranges.h new file mode 100644 index 0000000..1b6d069 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/metrics/bucket_ranges.h @@ -0,0 +1,105 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// BucketRanges stores the vector of ranges that delimit what samples are +// tallied in the corresponding buckets of a histogram. Histograms that have +// same ranges for all their corresponding buckets should share the same +// BucketRanges object. +// +// E.g. A 5 buckets LinearHistogram with 1 as minimal value and 4 as maximal +// value will need a BucketRanges with 6 ranges: +// 0, 1, 2, 3, 4, INT_MAX +// +// TODO(kaiwang): Currently we keep all negative values in 0~1 bucket. Consider +// changing 0 to INT_MIN. + +#ifndef BASE_METRICS_BUCKET_RANGES_H_ +#define BASE_METRICS_BUCKET_RANGES_H_ + +#include +#include + +#include + +#include + +#include "base/atomicops.h" +#include "base/base_export.h" +#include "base/macros.h" +#include "base/metrics/histogram_base.h" + +namespace base { + +class BASE_EXPORT BucketRanges { + public: + typedef std::vector Ranges; + + explicit BucketRanges(size_t num_ranges); + ~BucketRanges(); + + size_t size() const { return ranges_.size(); } + HistogramBase::Sample range(size_t i) const { return ranges_[i]; } + void set_range(size_t i, HistogramBase::Sample value) { + DCHECK_LT(i, ranges_.size()); + DCHECK_GE(value, 0); + ranges_[i] = value; + } + uint32_t checksum() const { return checksum_; } + void set_checksum(uint32_t checksum) { checksum_ = checksum; } + + // A bucket is defined by a consecutive pair of entries in |ranges|, so there + // is one fewer bucket than there are ranges. For example, if |ranges| is + // [0, 1, 3, 7, INT_MAX], then the buckets in this histogram are + // [0, 1), [1, 3), [3, 7), and [7, INT_MAX). + size_t bucket_count() const { return ranges_.size() - 1; } + + // Checksum methods to verify whether the ranges are corrupted (e.g. bad + // memory access). + uint32_t CalculateChecksum() const; + bool HasValidChecksum() const; + void ResetChecksum(); + + // Return true iff |other| object has same ranges_ as |this| object's ranges_. + bool Equals(const BucketRanges* other) const; + + // Set and get a reference into persistent memory where this bucket data + // can be found (and re-used). These calls are internally atomic with no + // safety against overwriting an existing value since though it is wasteful + // to have multiple identical persistent records, it is still safe. + void set_persistent_reference(uint32_t ref) const { + subtle::Release_Store(&persistent_reference_, ref); + } + uint32_t persistent_reference() const { + return subtle::Acquire_Load(&persistent_reference_); + } + + private: + // A monotonically increasing list of values which determine which bucket to + // put a sample into. For each index, show the smallest sample that can be + // added to the corresponding bucket. + Ranges ranges_; + + // Checksum for the conntents of ranges_. Used to detect random over-writes + // of our data, and to quickly see if some other BucketRanges instance is + // possibly Equal() to this instance. + // TODO(kaiwang): Consider change this to uint64_t. Because we see a lot of + // noise on UMA dashboard. + uint32_t checksum_; + + // A reference into a global PersistentMemoryAllocator where the ranges + // information is stored. This allows for the record to be created once and + // re-used simply by having all histograms with the same ranges use the + // same reference. + mutable subtle::Atomic32 persistent_reference_ = 0; + + DISALLOW_COPY_AND_ASSIGN(BucketRanges); +}; + +////////////////////////////////////////////////////////////////////////////// +// Expose only for test. +BASE_EXPORT extern const uint32_t kCrcTable[256]; + +} // namespace base + +#endif // BASE_METRICS_BUCKET_RANGES_H_ diff --git a/native/zucchini/vendor/libchrome/base/metrics/field_trial.h b/native/zucchini/vendor/libchrome/base/metrics/field_trial.h new file mode 100644 index 0000000..ac4ea1c --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/metrics/field_trial.h @@ -0,0 +1,802 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// FieldTrial is a class for handling details of statistical experiments +// performed by actual users in the field (i.e., in a shipped or beta product). +// All code is called exclusively on the UI thread currently. +// +// The simplest example is an experiment to see whether one of two options +// produces "better" results across our user population. In that scenario, UMA +// data is uploaded to aggregate the test results, and this FieldTrial class +// manages the state of each such experiment (state == which option was +// pseudo-randomly selected). +// +// States are typically generated randomly, either based on a one time +// randomization (which will yield the same results, in terms of selecting +// the client for a field trial or not, for every run of the program on a +// given machine), or by a session randomization (generated each time the +// application starts up, but held constant during the duration of the +// process). + +//------------------------------------------------------------------------------ +// Example: Suppose we have an experiment involving memory, such as determining +// the impact of some pruning algorithm. +// We assume that we already have a histogram of memory usage, such as: + +// UMA_HISTOGRAM_COUNTS("Memory.RendererTotal", count); + +// Somewhere in main thread initialization code, we'd probably define an +// instance of a FieldTrial, with code such as: + +// // FieldTrials are reference counted, and persist automagically until +// // process teardown, courtesy of their automatic registration in +// // FieldTrialList. +// // Note: This field trial will run in Chrome instances compiled through +// // 8 July, 2015, and after that all instances will be in "StandardMem". +// scoped_refptr trial( +// base::FieldTrialList::FactoryGetFieldTrial( +// "MemoryExperiment", 1000, "StandardMem", 2015, 7, 8, +// base::FieldTrial::ONE_TIME_RANDOMIZED, NULL)); +// +// const int high_mem_group = +// trial->AppendGroup("HighMem", 20); // 2% in HighMem group. +// const int low_mem_group = +// trial->AppendGroup("LowMem", 20); // 2% in LowMem group. +// // Take action depending of which group we randomly land in. +// if (trial->group() == high_mem_group) +// SetPruningAlgorithm(kType1); // Sample setting of browser state. +// else if (trial->group() == low_mem_group) +// SetPruningAlgorithm(kType2); // Sample alternate setting. + +//------------------------------------------------------------------------------ + +#ifndef BASE_METRICS_FIELD_TRIAL_H_ +#define BASE_METRICS_FIELD_TRIAL_H_ + +#include +#include + +#include +#include +#include +#include +#include + +#include "base/atomicops.h" +#include "base/base_export.h" +#include "base/command_line.h" +#include "base/feature_list.h" +#include "base/files/file.h" +#include "base/gtest_prod_util.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/shared_memory.h" +#include "base/memory/shared_memory_handle.h" +#include "base/metrics/persistent_memory_allocator.h" +#include "base/observer_list_threadsafe.h" +#include "base/pickle.h" +#include "base/process/launch.h" +#include "base/strings/string_piece.h" +#include "base/synchronization/lock.h" +#include "base/time/time.h" +#include "build/build_config.h" + +namespace base { + +class FieldTrialList; + +class BASE_EXPORT FieldTrial : public RefCounted { + public: + typedef int Probability; // Probability type for being selected in a trial. + + // TODO(665129): Make private again after crash has been resolved. + typedef SharedPersistentMemoryAllocator::Reference FieldTrialRef; + + // Specifies the persistence of the field trial group choice. + enum RandomizationType { + // One time randomized trials will persist the group choice between + // restarts, which is recommended for most trials, especially those that + // change user visible behavior. + ONE_TIME_RANDOMIZED, + // Session randomized trials will roll the dice to select a group on every + // process restart. + SESSION_RANDOMIZED, + }; + + // EntropyProvider is an interface for providing entropy for one-time + // randomized (persistent) field trials. + class BASE_EXPORT EntropyProvider { + public: + virtual ~EntropyProvider(); + + // Returns a double in the range of [0, 1) to be used for the dice roll for + // the specified field trial. If |randomization_seed| is not 0, it will be + // used in preference to |trial_name| for generating the entropy by entropy + // providers that support it. A given instance should always return the same + // value given the same input |trial_name| and |randomization_seed| values. + virtual double GetEntropyForTrial(const std::string& trial_name, + uint32_t randomization_seed) const = 0; + }; + + // A pair representing a Field Trial and its selected group. + struct ActiveGroup { + std::string trial_name; + std::string group_name; + }; + + // A triplet representing a FieldTrial, its selected group and whether it's + // active. String members are pointers to the underlying strings owned by the + // FieldTrial object. Does not use StringPiece to avoid conversions back to + // std::string. + struct BASE_EXPORT State { + const std::string* trial_name = nullptr; + const std::string* group_name = nullptr; + bool activated = false; + + State(); + State(const State& other); + ~State(); + }; + + // We create one FieldTrialEntry per field trial in shared memory, via + // AddToAllocatorWhileLocked. The FieldTrialEntry is followed by a + // base::Pickle object that we unpickle and read from. + struct BASE_EXPORT FieldTrialEntry { + // SHA1(FieldTrialEntry): Increment this if structure changes! + static constexpr uint32_t kPersistentTypeId = 0xABA17E13 + 2; + + // Expected size for 32/64-bit check. + static constexpr size_t kExpectedInstanceSize = 8; + + // Whether or not this field trial is activated. This is really just a + // boolean but using a 32 bit value for portability reasons. It should be + // accessed via NoBarrier_Load()/NoBarrier_Store() to prevent the compiler + // from doing unexpected optimizations because it thinks that only one + // thread is accessing the memory location. + subtle::Atomic32 activated; + + // Size of the pickled structure, NOT the total size of this entry. + uint32_t pickle_size; + + // Calling this is only valid when the entry is initialized. That is, it + // resides in shared memory and has a pickle containing the trial name and + // group name following it. + bool GetTrialAndGroupName(StringPiece* trial_name, + StringPiece* group_name) const; + + // Calling this is only valid when the entry is initialized as well. Reads + // the parameters following the trial and group name and stores them as + // key-value mappings in |params|. + bool GetParams(std::map* params) const; + + private: + // Returns an iterator over the data containing names and params. + PickleIterator GetPickleIterator() const; + + // Takes the iterator and writes out the first two items into |trial_name| + // and |group_name|. + bool ReadStringPair(PickleIterator* iter, + StringPiece* trial_name, + StringPiece* group_name) const; + }; + + typedef std::vector ActiveGroups; + + // A return value to indicate that a given instance has not yet had a group + // assignment (and hence is not yet participating in the trial). + static const int kNotFinalized; + + // Disables this trial, meaning it always determines the default group + // has been selected. May be called immediately after construction, or + // at any time after initialization (should not be interleaved with + // AppendGroup calls). Once disabled, there is no way to re-enable a + // trial. + // TODO(mad): http://code.google.com/p/chromium/issues/detail?id=121446 + // This doesn't properly reset to Default when a group was forced. + void Disable(); + + // Establish the name and probability of the next group in this trial. + // Sometimes, based on construction randomization, this call may cause the + // provided group to be *THE* group selected for use in this instance. + // The return value is the group number of the new group. + int AppendGroup(const std::string& name, Probability group_probability); + + // Return the name of the FieldTrial (excluding the group name). + const std::string& trial_name() const { return trial_name_; } + + // Return the randomly selected group number that was assigned, and notify + // any/all observers that this finalized group number has presumably been used + // (queried), and will never change. Note that this will force an instance to + // participate, and make it illegal to attempt to probabilistically add any + // other groups to the trial. + int group(); + + // If the group's name is empty, a string version containing the group number + // is used as the group name. This causes a winner to be chosen if none was. + const std::string& group_name(); + + // Finalizes the group choice and returns the chosen group, but does not mark + // the trial as active - so its state will not be reported until group_name() + // or similar is called. + const std::string& GetGroupNameWithoutActivation(); + + // Set the field trial as forced, meaning that it was setup earlier than + // the hard coded registration of the field trial to override it. + // This allows the code that was hard coded to register the field trial to + // still succeed even though the field trial has already been registered. + // This must be called after appending all the groups, since we will make + // the group choice here. Note that this is a NOOP for already forced trials. + // And, as the rest of the FieldTrial code, this is not thread safe and must + // be done from the UI thread. + void SetForced(); + + // Enable benchmarking sets field trials to a common setting. + static void EnableBenchmarking(); + + // Creates a FieldTrial object with the specified parameters, to be used for + // simulation of group assignment without actually affecting global field + // trial state in the running process. Group assignment will be done based on + // |entropy_value|, which must have a range of [0, 1). + // + // Note: Using this function will not register the field trial globally in the + // running process - for that, use FieldTrialList::FactoryGetFieldTrial(). + // + // The ownership of the returned FieldTrial is transfered to the caller which + // is responsible for deref'ing it (e.g. by using scoped_refptr). + static FieldTrial* CreateSimulatedFieldTrial( + const std::string& trial_name, + Probability total_probability, + const std::string& default_group_name, + double entropy_value); + + private: + // Allow tests to access our innards for testing purposes. + FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, Registration); + FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, AbsoluteProbabilities); + FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, RemainingProbability); + FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, FiftyFiftyProbability); + FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, MiddleProbabilities); + FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, OneWinner); + FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, DisableProbability); + FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, ActiveGroups); + FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, AllGroups); + FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, ActiveGroupsNotFinalized); + FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, Save); + FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SaveAll); + FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, DuplicateRestore); + FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedTurnFeatureOff); + FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedTurnFeatureOn); + FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedChangeDefault_Default); + FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedChangeDefault_NonDefault); + FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, FloatBoundariesGiveEqualGroupSizes); + FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, DoesNotSurpassTotalProbability); + FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, + DoNotAddSimulatedFieldTrialsToAllocator); + FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, ClearParamsFromSharedMemory); + + friend class base::FieldTrialList; + + friend class RefCounted; + + // This is the group number of the 'default' group when a choice wasn't forced + // by a call to FieldTrialList::CreateFieldTrial. It is kept private so that + // consumers don't use it by mistake in cases where the group was forced. + static const int kDefaultGroupNumber; + + // Creates a field trial with the specified parameters. Group assignment will + // be done based on |entropy_value|, which must have a range of [0, 1). + FieldTrial(const std::string& trial_name, + Probability total_probability, + const std::string& default_group_name, + double entropy_value); + virtual ~FieldTrial(); + + // Return the default group name of the FieldTrial. + std::string default_group_name() const { return default_group_name_; } + + // Marks this trial as having been registered with the FieldTrialList. Must be + // called no more than once and before any |group()| calls have occurred. + void SetTrialRegistered(); + + // Sets the chosen group name and number. + void SetGroupChoice(const std::string& group_name, int number); + + // Ensures that a group is chosen, if it hasn't yet been. The field trial + // might yet be disabled, so this call will *not* notify observers of the + // status. + void FinalizeGroupChoice(); + + // Implements FinalizeGroupChoice() with the added flexibility of being + // deadlock-free if |is_locked| is true and the caller is holding a lock. + void FinalizeGroupChoiceImpl(bool is_locked); + + // Returns the trial name and selected group name for this field trial via + // the output parameter |active_group|, but only if the group has already + // been chosen and has been externally observed via |group()| and the trial + // has not been disabled. In that case, true is returned and |active_group| + // is filled in; otherwise, the result is false and |active_group| is left + // untouched. + bool GetActiveGroup(ActiveGroup* active_group) const; + + // Returns the trial name and selected group name for this field trial via + // the output parameter |field_trial_state| for all the studies when + // |bool include_expired| is true. In case when |bool include_expired| is + // false, if the trial has not been disabled true is returned and + // |field_trial_state| is filled in; otherwise, the result is false and + // |field_trial_state| is left untouched. + // This function is deadlock-free if the caller is holding a lock. + bool GetStateWhileLocked(State* field_trial_state, bool include_expired); + + // Returns the group_name. A winner need not have been chosen. + std::string group_name_internal() const { return group_name_; } + + // The name of the field trial, as can be found via the FieldTrialList. + const std::string trial_name_; + + // The maximum sum of all probabilities supplied, which corresponds to 100%. + // This is the scaling factor used to adjust supplied probabilities. + const Probability divisor_; + + // The name of the default group. + const std::string default_group_name_; + + // The randomly selected probability that is used to select a group (or have + // the instance not participate). It is the product of divisor_ and a random + // number between [0, 1). + Probability random_; + + // Sum of the probabilities of all appended groups. + Probability accumulated_group_probability_; + + // The number that will be returned by the next AppendGroup() call. + int next_group_number_; + + // The pseudo-randomly assigned group number. + // This is kNotFinalized if no group has been assigned. + int group_; + + // A textual name for the randomly selected group. Valid after |group()| + // has been called. + std::string group_name_; + + // When enable_field_trial_ is false, field trial reverts to the 'default' + // group. + bool enable_field_trial_; + + // When forced_ is true, we return the chosen group from AppendGroup when + // appropriate. + bool forced_; + + // Specifies whether the group choice has been reported to observers. + bool group_reported_; + + // Whether this trial is registered with the global FieldTrialList and thus + // should notify it when its group is queried. + bool trial_registered_; + + // Reference to related field trial struct and data in shared memory. + FieldTrialRef ref_; + + // When benchmarking is enabled, field trials all revert to the 'default' + // group. + static bool enable_benchmarking_; + + DISALLOW_COPY_AND_ASSIGN(FieldTrial); +}; + +//------------------------------------------------------------------------------ +// Class with a list of all active field trials. A trial is active if it has +// been registered, which includes evaluating its state based on its probaility. +// Only one instance of this class exists and outside of testing, will live for +// the entire life time of the process. +class BASE_EXPORT FieldTrialList { + public: + typedef SharedPersistentMemoryAllocator FieldTrialAllocator; + + // Type for function pointer passed to |AllParamsToString| used to escape + // special characters from |input|. + typedef std::string (*EscapeDataFunc)(const std::string& input); + + // Year that is guaranteed to not be expired when instantiating a field trial + // via |FactoryGetFieldTrial()|. Set to two years from the build date. + static int kNoExpirationYear; + + // Observer is notified when a FieldTrial's group is selected. + class BASE_EXPORT Observer { + public: + // Notify observers when FieldTrials's group is selected. + virtual void OnFieldTrialGroupFinalized(const std::string& trial_name, + const std::string& group_name) = 0; + + protected: + virtual ~Observer(); + }; + + // This singleton holds the global list of registered FieldTrials. + // + // To support one-time randomized field trials, specify a non-null + // |entropy_provider| which should be a source of uniformly distributed + // entropy values. If one time randomization is not desired, pass in null for + // |entropy_provider|. + explicit FieldTrialList( + std::unique_ptr entropy_provider); + + // Destructor Release()'s references to all registered FieldTrial instances. + ~FieldTrialList(); + + // Get a FieldTrial instance from the factory. + // + // |name| is used to register the instance with the FieldTrialList class, + // and can be used to find the trial (only one trial can be present for each + // name). |default_group_name| is the name of the default group which will + // be chosen if none of the subsequent appended groups get to be chosen. + // |default_group_number| can receive the group number of the default group as + // AppendGroup returns the number of the subsequence groups. |trial_name| and + // |default_group_name| may not be empty but |default_group_number| can be + // NULL if the value is not needed. + // + // Group probabilities that are later supplied must sum to less than or equal + // to the |total_probability|. Arguments |year|, |month| and |day_of_month| + // specify the expiration time. If the build time is after the expiration time + // then the field trial reverts to the 'default' group. + // + // Use this static method to get a startup-randomized FieldTrial or a + // previously created forced FieldTrial. + static FieldTrial* FactoryGetFieldTrial( + const std::string& trial_name, + FieldTrial::Probability total_probability, + const std::string& default_group_name, + const int year, + const int month, + const int day_of_month, + FieldTrial::RandomizationType randomization_type, + int* default_group_number); + + // Same as FactoryGetFieldTrial(), but allows specifying a custom seed to be + // used on one-time randomized field trials (instead of a hash of the trial + // name, which is used otherwise or if |randomization_seed| has value 0). The + // |randomization_seed| value (other than 0) should never be the same for two + // trials, else this would result in correlated group assignments. Note: + // Using a custom randomization seed is only supported by the + // PermutedEntropyProvider (which is used when UMA is not enabled). If + // |override_entropy_provider| is not null, then it will be used for + // randomization instead of the provider given when the FieldTrialList was + // instantiated. + static FieldTrial* FactoryGetFieldTrialWithRandomizationSeed( + const std::string& trial_name, + FieldTrial::Probability total_probability, + const std::string& default_group_name, + const int year, + const int month, + const int day_of_month, + FieldTrial::RandomizationType randomization_type, + uint32_t randomization_seed, + int* default_group_number, + const FieldTrial::EntropyProvider* override_entropy_provider); + + // The Find() method can be used to test to see if a named trial was already + // registered, or to retrieve a pointer to it from the global map. + static FieldTrial* Find(const std::string& trial_name); + + // Returns the group number chosen for the named trial, or + // FieldTrial::kNotFinalized if the trial does not exist. + static int FindValue(const std::string& trial_name); + + // Returns the group name chosen for the named trial, or the empty string if + // the trial does not exist. The first call of this function on a given field + // trial will mark it as active, so that its state will be reported with usage + // metrics, crashes, etc. + static std::string FindFullName(const std::string& trial_name); + + // Returns true if the named trial has been registered. + static bool TrialExists(const std::string& trial_name); + + // Returns true if the named trial exists and has been activated. + static bool IsTrialActive(const std::string& trial_name); + + // Creates a persistent representation of active FieldTrial instances for + // resurrection in another process. This allows randomization to be done in + // one process, and secondary processes can be synchronized on the result. + // The resulting string contains the name and group name pairs of all + // registered FieldTrials for which the group has been chosen and externally + // observed (via |group()|) and which have not been disabled, with "/" used + // to separate all names and to terminate the string. This string is parsed + // by |CreateTrialsFromString()|. + static void StatesToString(std::string* output); + + // Creates a persistent representation of all FieldTrial instances for + // resurrection in another process. This allows randomization to be done in + // one process, and secondary processes can be synchronized on the result. + // The resulting string contains the name and group name pairs of all + // registered FieldTrials including disabled based on |include_expired|, + // with "/" used to separate all names and to terminate the string. All + // activated trials have their name prefixed with "*". This string is parsed + // by |CreateTrialsFromString()|. + static void AllStatesToString(std::string* output, bool include_expired); + + // Creates a persistent representation of all FieldTrial params for + // resurrection in another process. The returned string contains the trial + // name and group name pairs of all registered FieldTrials including disabled + // based on |include_expired| separated by '.'. The pair is followed by ':' + // separator and list of param name and values separated by '/'. It also takes + // |encode_data_func| function pointer for encodeing special charactors. + // This string is parsed by |AssociateParamsFromString()|. + static std::string AllParamsToString(bool include_expired, + EscapeDataFunc encode_data_func); + + // Fills in the supplied vector |active_groups| (which must be empty when + // called) with a snapshot of all registered FieldTrials for which the group + // has been chosen and externally observed (via |group()|) and which have + // not been disabled. + static void GetActiveFieldTrialGroups( + FieldTrial::ActiveGroups* active_groups); + + // Returns the field trials that are marked active in |trials_string|. + static void GetActiveFieldTrialGroupsFromString( + const std::string& trials_string, + FieldTrial::ActiveGroups* active_groups); + + // Returns the field trials that were active when the process was + // created. Either parses the field trial string or the shared memory + // holding field trial information. + // Must be called only after a call to CreateTrialsFromCommandLine(). + static void GetInitiallyActiveFieldTrials( + const base::CommandLine& command_line, + FieldTrial::ActiveGroups* active_groups); + + // Use a state string (re: StatesToString()) to augment the current list of + // field trials to include the supplied trials, and using a 100% probability + // for each trial, force them to have the same group string. This is commonly + // used in a non-browser process, to carry randomly selected state in a + // browser process into this non-browser process, but could also be invoked + // through a command line argument to the browser process. Created field + // trials will be marked "used" for the purposes of active trial reporting + // if they are prefixed with |kActivationMarker|. Trial names in + // |ignored_trial_names| are ignored when parsing |trials_string|. + static bool CreateTrialsFromString( + const std::string& trials_string, + const std::set& ignored_trial_names); + + // Achieves the same thing as CreateTrialsFromString, except wraps the logic + // by taking in the trials from the command line, either via shared memory + // handle or command line argument. A bit of a misnomer since on POSIX we + // simply get the trials from opening |fd_key| if using shared memory. On + // Windows, we expect the |cmd_line| switch for |field_trial_handle_switch| to + // contain the shared memory handle that contains the field trial allocator. + // We need the |field_trial_handle_switch| and |fd_key| arguments to be passed + // in since base/ can't depend on content/. + static void CreateTrialsFromCommandLine(const base::CommandLine& cmd_line, + const char* field_trial_handle_switch, + int fd_key); + + // Creates base::Feature overrides from the command line by first trying to + // use shared memory and then falling back to the command line if it fails. + static void CreateFeaturesFromCommandLine( + const base::CommandLine& command_line, + const char* enable_features_switch, + const char* disable_features_switch, + FeatureList* feature_list); + +#if defined(OS_WIN) + // On Windows, we need to explicitly pass down any handles to be inherited. + // This function adds the shared memory handle to field trial state to the + // list of handles to be inherited. + static void AppendFieldTrialHandleIfNeeded( + base::HandlesToInheritVector* handles); +#elif defined(OS_FUCHSIA) + // TODO(fuchsia): Implement shared-memory configuration (crbug.com/752368). +#elif defined(OS_POSIX) && !defined(OS_NACL) + // On POSIX, we also need to explicitly pass down this file descriptor that + // should be shared with the child process. Returns an invalid handle if it + // was not initialized properly. + static base::SharedMemoryHandle GetFieldTrialHandle(); +#endif + + // Adds a switch to the command line containing the field trial state as a + // string (if not using shared memory to share field trial state), or the + // shared memory handle + length. + // Needs the |field_trial_handle_switch| argument to be passed in since base/ + // can't depend on content/. + static void CopyFieldTrialStateToFlags(const char* field_trial_handle_switch, + const char* enable_features_switch, + const char* disable_features_switch, + base::CommandLine* cmd_line); + + // Create a FieldTrial with the given |name| and using 100% probability for + // the FieldTrial, force FieldTrial to have the same group string as + // |group_name|. This is commonly used in a non-browser process, to carry + // randomly selected state in a browser process into this non-browser process. + // It returns NULL if there is a FieldTrial that is already registered with + // the same |name| but has different finalized group string (|group_name|). + static FieldTrial* CreateFieldTrial(const std::string& name, + const std::string& group_name); + + // Add an observer to be notified when a field trial is irrevocably committed + // to being part of some specific field_group (and hence the group_name is + // also finalized for that field_trial). Returns false and does nothing if + // there is no FieldTrialList singleton. + static bool AddObserver(Observer* observer); + + // Remove an observer. + static void RemoveObserver(Observer* observer); + + // Similar to AddObserver(), but the passed observer will be notified + // synchronously when a field trial is activated and its group selected. It + // will be notified synchronously on the same thread where the activation and + // group selection happened. It is the responsibility of the observer to make + // sure that this is a safe operation and the operation must be fast, as this + // work is done synchronously as part of group() or related APIs. Only a + // single such observer is supported, exposed specifically for crash + // reporting. Must be called on the main thread before any other threads + // have been started. + static void SetSynchronousObserver(Observer* observer); + + // Removes the single synchronous observer. + static void RemoveSynchronousObserver(Observer* observer); + + // Grabs the lock if necessary and adds the field trial to the allocator. This + // should only be called from FinalizeGroupChoice(). + static void OnGroupFinalized(bool is_locked, FieldTrial* field_trial); + + // Notify all observers that a group has been finalized for |field_trial|. + static void NotifyFieldTrialGroupSelection(FieldTrial* field_trial); + + // Return the number of active field trials. + static size_t GetFieldTrialCount(); + + // Gets the parameters for |field_trial| from shared memory and stores them in + // |params|. This is only exposed for use by FieldTrialParamAssociator and + // shouldn't be used by anything else. + static bool GetParamsFromSharedMemory( + FieldTrial* field_trial, + std::map* params); + + // Clears all the params in the allocator. + static void ClearParamsFromSharedMemoryForTesting(); + + // Dumps field trial state to an allocator so that it can be analyzed after a + // crash. + static void DumpAllFieldTrialsToPersistentAllocator( + PersistentMemoryAllocator* allocator); + + // Retrieves field trial state from an allocator so that it can be analyzed + // after a crash. The pointers in the returned vector are into the persistent + // memory segment and so are only valid as long as the allocator is valid. + static std::vector + GetAllFieldTrialsFromPersistentAllocator( + PersistentMemoryAllocator const& allocator); + + // Returns true if a global field trial list is set. Only used for testing. + static bool IsGlobalSetForTesting(); + + private: + // Allow tests to access our innards for testing purposes. + FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, InstantiateAllocator); + FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, AddTrialsToAllocator); + FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, + DoNotAddSimulatedFieldTrialsToAllocator); + FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, AssociateFieldTrialParams); + FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, ClearParamsFromSharedMemory); + FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, + SerializeSharedMemoryHandleMetadata); + FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, CheckReadOnlySharedMemoryHandle); + + // Serialization is used to pass information about the handle to child + // processes. It passes a reference to the relevant OS resource, and it passes + // a GUID. Serialization and deserialization doesn't actually transport the + // underlying OS resource - that must be done by the Process launcher. + static std::string SerializeSharedMemoryHandleMetadata( + const SharedMemoryHandle& shm); +#if defined(OS_WIN) || defined(OS_FUCHSIA) + static SharedMemoryHandle DeserializeSharedMemoryHandleMetadata( + const std::string& switch_value); +#elif defined(OS_POSIX) && !defined(OS_NACL) + static SharedMemoryHandle DeserializeSharedMemoryHandleMetadata( + int fd, + const std::string& switch_value); +#endif + +#if defined(OS_WIN) || defined(OS_FUCHSIA) + // Takes in |handle_switch| from the command line which represents the shared + // memory handle for field trials, parses it, and creates the field trials. + // Returns true on success, false on failure. + // |switch_value| also contains the serialized GUID. + static bool CreateTrialsFromSwitchValue(const std::string& switch_value); +#elif defined(OS_POSIX) && !defined(OS_NACL) + // On POSIX systems that use the zygote, we look up the correct fd that backs + // the shared memory segment containing the field trials by looking it up via + // an fd key in GlobalDescriptors. Returns true on success, false on failure. + // |switch_value| also contains the serialized GUID. + static bool CreateTrialsFromDescriptor(int fd_key, + const std::string& switch_value); +#endif + + // Takes an unmapped SharedMemoryHandle, creates a SharedMemory object from it + // and maps it with the correct size. + static bool CreateTrialsFromSharedMemoryHandle(SharedMemoryHandle shm_handle); + + // Expects a mapped piece of shared memory |shm| that was created from the + // browser process's field_trial_allocator and shared via the command line. + // This function recreates the allocator, iterates through all the field + // trials in it, and creates them via CreateFieldTrial(). Returns true if + // successful and false otherwise. + static bool CreateTrialsFromSharedMemory( + std::unique_ptr shm); + + // Instantiate the field trial allocator, add all existing field trials to it, + // and duplicates its handle to a read-only handle, which gets stored in + // |readonly_allocator_handle|. + static void InstantiateFieldTrialAllocatorIfNeeded(); + + // Adds the field trial to the allocator. Caller must hold a lock before + // calling this. + static void AddToAllocatorWhileLocked(PersistentMemoryAllocator* allocator, + FieldTrial* field_trial); + + // Activate the corresponding field trial entry struct in shared memory. + static void ActivateFieldTrialEntryWhileLocked(FieldTrial* field_trial); + + // A map from FieldTrial names to the actual instances. + typedef std::map RegistrationMap; + + // If one-time randomization is enabled, returns a weak pointer to the + // corresponding EntropyProvider. Otherwise, returns NULL. + static const FieldTrial::EntropyProvider* + GetEntropyProviderForOneTimeRandomization(); + + // Helper function should be called only while holding lock_. + FieldTrial* PreLockedFind(const std::string& name); + + // Register() stores a pointer to the given trial in a global map. + // This method also AddRef's the indicated trial. + // This should always be called after creating a new FieldTrial instance. + static void Register(FieldTrial* trial); + + // Returns all the registered trials. + static RegistrationMap GetRegisteredTrials(); + + static FieldTrialList* global_; // The singleton of this class. + + // This will tell us if there is an attempt to register a field + // trial or check if one-time randomization is enabled without + // creating the FieldTrialList. This is not an error, unless a + // FieldTrialList is created after that. + static bool used_without_global_; + + // Lock for access to registered_ and field_trial_allocator_. + Lock lock_; + RegistrationMap registered_; + + std::map seen_states_; + + // Entropy provider to be used for one-time randomized field trials. If NULL, + // one-time randomization is not supported. + std::unique_ptr entropy_provider_; + + // List of observers to be notified when a group is selected for a FieldTrial. + scoped_refptr > observer_list_; + + // Single synchronous observer to be notified when a trial group is chosen. + Observer* synchronous_observer_ = nullptr; + + // Allocator in shared memory containing field trial data. Used in both + // browser and child processes, but readonly in the child. + // In the future, we may want to move this to a more generic place if we want + // to start passing more data other than field trials. + std::unique_ptr field_trial_allocator_ = nullptr; + + // Readonly copy of the handle to the allocator. Needs to be a member variable + // because it's needed from both CopyFieldTrialStateToFlags() and + // AppendFieldTrialHandleIfNeeded(). + base::SharedMemoryHandle readonly_allocator_handle_; + + // Tracks whether CreateTrialsFromCommandLine() has been called. + bool create_trials_from_command_line_called_ = false; + + DISALLOW_COPY_AND_ASSIGN(FieldTrialList); +}; + +} // namespace base + +#endif // BASE_METRICS_FIELD_TRIAL_H_ diff --git a/native/zucchini/vendor/libchrome/base/metrics/histogram.h b/native/zucchini/vendor/libchrome/base/metrics/histogram.h new file mode 100644 index 0000000..bd5a9eb --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/metrics/histogram.h @@ -0,0 +1,610 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Histogram is an object that aggregates statistics, and can summarize them in +// various forms, including ASCII graphical, HTML, and numerically (as a +// vector of numbers corresponding to each of the aggregating buckets). + +// It supports calls to accumulate either time intervals (which are processed +// as integral number of milliseconds), or arbitrary integral units. + +// For Histogram (exponential histogram), LinearHistogram and CustomHistogram, +// the minimum for a declared range is 1 (instead of 0), while the maximum is +// (HistogramBase::kSampleType_MAX - 1). However, there will always be underflow +// and overflow buckets added automatically, so a 0 bucket will always exist +// even when a minimum value of 1 is specified. + +// Each use of a histogram with the same name will reference the same underlying +// data, so it is safe to record to the same histogram from multiple locations +// in the code. It is a runtime error if all uses of the same histogram do not +// agree exactly in type, bucket size and range. + +// For Histogram and LinearHistogram, the maximum for a declared range should +// always be larger (not equal) than minimal range. Zero and +// HistogramBase::kSampleType_MAX are implicitly added as first and last ranges, +// so the smallest legal bucket_count is 3. However CustomHistogram can have +// bucket count as 2 (when you give a custom ranges vector containing only 1 +// range). +// For these 3 kinds of histograms, the max bucket count is always +// (Histogram::kBucketCount_MAX - 1). + +// The buckets layout of class Histogram is exponential. For example, buckets +// might contain (sequentially) the count of values in the following intervals: +// [0,1), [1,2), [2,4), [4,8), [8,16), [16,32), [32,64), [64,infinity) +// That bucket allocation would actually result from construction of a histogram +// for values between 1 and 64, with 8 buckets, such as: +// Histogram count("some name", 1, 64, 8); +// Note that the underflow bucket [0,1) and the overflow bucket [64,infinity) +// are also counted by the constructor in the user supplied "bucket_count" +// argument. +// The above example has an exponential ratio of 2 (doubling the bucket width +// in each consecutive bucket). The Histogram class automatically calculates +// the smallest ratio that it can use to construct the number of buckets +// selected in the constructor. An another example, if you had 50 buckets, +// and millisecond time values from 1 to 10000, then the ratio between +// consecutive bucket widths will be approximately somewhere around the 50th +// root of 10000. This approach provides very fine grain (narrow) buckets +// at the low end of the histogram scale, but allows the histogram to cover a +// gigantic range with the addition of very few buckets. + +// Usually we use macros to define and use a histogram, which are defined in +// base/metrics/histogram_macros.h. Note: Callers should include that header +// directly if they only access the histogram APIs through macros. +// +// Macros use a pattern involving a function static variable, that is a pointer +// to a histogram. This static is explicitly initialized on any thread +// that detects a uninitialized (NULL) pointer. The potentially racy +// initialization is not a problem as it is always set to point to the same +// value (i.e., the FactoryGet always returns the same value). FactoryGet +// is also completely thread safe, which results in a completely thread safe, +// and relatively fast, set of counters. To avoid races at shutdown, the static +// pointer is NOT deleted, and we leak the histograms at process termination. + +#ifndef BASE_METRICS_HISTOGRAM_H_ +#define BASE_METRICS_HISTOGRAM_H_ + +#include +#include + +#include +#include +#include +#include + +#include "base/base_export.h" +#include "base/compiler_specific.h" +#include "base/containers/span.h" +#include "base/gtest_prod_util.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/metrics/bucket_ranges.h" +#include "base/metrics/histogram_base.h" +#include "base/metrics/histogram_samples.h" +#include "base/strings/string_piece.h" +#include "base/time/time.h" + +namespace base { + +class BooleanHistogram; +class CustomHistogram; +class DelayedPersistentAllocation; +class Histogram; +class HistogramTest; +class LinearHistogram; +class Pickle; +class PickleIterator; +class SampleVector; +class SampleVectorBase; + +class BASE_EXPORT Histogram : public HistogramBase { + public: + // Initialize maximum number of buckets in histograms as 16,384. + static const uint32_t kBucketCount_MAX; + + typedef std::vector Counts; + + ~Histogram() override; + + //---------------------------------------------------------------------------- + // For a valid histogram, input should follow these restrictions: + // minimum > 0 (if a minimum below 1 is specified, it will implicitly be + // normalized up to 1) + // maximum > minimum + // buckets > 2 [minimum buckets needed: underflow, overflow and the range] + // Additionally, + // buckets <= (maximum - minimum + 2) - this is to ensure that we don't have + // more buckets than the range of numbers; having more buckets than 1 per + // value in the range would be nonsensical. + static HistogramBase* FactoryGet(const std::string& name, + Sample minimum, + Sample maximum, + uint32_t bucket_count, + int32_t flags); + static HistogramBase* FactoryTimeGet(const std::string& name, + base::TimeDelta minimum, + base::TimeDelta maximum, + uint32_t bucket_count, + int32_t flags); + static HistogramBase* FactoryMicrosecondsTimeGet(const std::string& name, + base::TimeDelta minimum, + base::TimeDelta maximum, + uint32_t bucket_count, + int32_t flags); + + // Overloads of the above functions that take a const char* |name| param, to + // avoid code bloat from the std::string constructor being inlined into call + // sites. + static HistogramBase* FactoryGet(const char* name, + Sample minimum, + Sample maximum, + uint32_t bucket_count, + int32_t flags); + static HistogramBase* FactoryTimeGet(const char* name, + base::TimeDelta minimum, + base::TimeDelta maximum, + uint32_t bucket_count, + int32_t flags); + static HistogramBase* FactoryMicrosecondsTimeGet(const char* name, + base::TimeDelta minimum, + base::TimeDelta maximum, + uint32_t bucket_count, + int32_t flags); + + // Create a histogram using data in persistent storage. + static std::unique_ptr PersistentCreate( + const char* name, + Sample minimum, + Sample maximum, + const BucketRanges* ranges, + const DelayedPersistentAllocation& counts, + const DelayedPersistentAllocation& logged_counts, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta); + + static void InitializeBucketRanges(Sample minimum, + Sample maximum, + BucketRanges* ranges); + + // This constant if for FindCorruption. Since snapshots of histograms are + // taken asynchronously relative to sampling, and our counting code currently + // does not prevent race conditions, it is pretty likely that we'll catch a + // redundant count that doesn't match the sample count. We allow for a + // certain amount of slop before flagging this as an inconsistency. Even with + // an inconsistency, we'll snapshot it again (for UMA in about a half hour), + // so we'll eventually get the data, if it was not the result of a corruption. + static const int kCommonRaceBasedCountMismatch; + + // Check to see if bucket ranges, counts and tallies in the snapshot are + // consistent with the bucket ranges and checksums in our histogram. This can + // produce a false-alarm if a race occurred in the reading of the data during + // a SnapShot process, but should otherwise be false at all times (unless we + // have memory over-writes, or DRAM failures). Flag definitions are located + // under "enum Inconsistency" in base/metrics/histogram_base.h. + uint32_t FindCorruption(const HistogramSamples& samples) const override; + + //---------------------------------------------------------------------------- + // Accessors for factory construction, serialization and testing. + //---------------------------------------------------------------------------- + const BucketRanges* bucket_ranges() const; + Sample declared_min() const; + Sample declared_max() const; + virtual Sample ranges(uint32_t i) const; + virtual uint32_t bucket_count() const; + + // This function validates histogram construction arguments. It returns false + // if some of the arguments are bad but also corrects them so they should + // function on non-dcheck builds without crashing. + // Note. Currently it allow some bad input, e.g. 0 as minimum, but silently + // converts it to good input: 1. + // TODO(bcwhite): Use false returns to create "sink" histograms so that bad + // data doesn't create confusion on the servers. + static bool InspectConstructionArguments(StringPiece name, + Sample* minimum, + Sample* maximum, + uint32_t* bucket_count); + + // HistogramBase implementation: + uint64_t name_hash() const override; + HistogramType GetHistogramType() const override; + bool HasConstructionArguments(Sample expected_minimum, + Sample expected_maximum, + uint32_t expected_bucket_count) const override; + void Add(Sample value) override; + void AddCount(Sample value, int count) override; + std::unique_ptr SnapshotSamples() const override; + std::unique_ptr SnapshotDelta() override; + std::unique_ptr SnapshotFinalDelta() const override; + void AddSamples(const HistogramSamples& samples) override; + bool AddSamplesFromPickle(base::PickleIterator* iter) override; + void WriteHTMLGraph(std::string* output) const override; + void WriteAscii(std::string* output) const override; + + // Validates the histogram contents and CHECKs on errors. + // TODO(bcwhite): Remove this after https://crbug/836875. + void ValidateHistogramContents() const override; + + protected: + // This class, defined entirely within the .cc file, contains all the + // common logic for building a Histogram and can be overridden by more + // specific types to alter details of how the creation is done. It is + // defined as an embedded class (rather than an anonymous one) so it + // can access the protected constructors. + class Factory; + + // |ranges| should contain the underflow and overflow buckets. See top + // comments for example. + Histogram(const char* name, + Sample minimum, + Sample maximum, + const BucketRanges* ranges); + + // Traditionally, histograms allocate their own memory for the bucket + // vector but "shared" histograms use memory regions allocated from a + // special memory segment that is passed in here. It is assumed that + // the life of this memory is managed externally and exceeds the lifetime + // of this object. Practically, this memory is never released until the + // process exits and the OS cleans it up. + Histogram(const char* name, + Sample minimum, + Sample maximum, + const BucketRanges* ranges, + const DelayedPersistentAllocation& counts, + const DelayedPersistentAllocation& logged_counts, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta); + + // HistogramBase implementation: + void SerializeInfoImpl(base::Pickle* pickle) const override; + + // Method to override to skip the display of the i'th bucket if it's empty. + virtual bool PrintEmptyBucket(uint32_t index) const; + + // Get normalized size, relative to the ranges(i). + virtual double GetBucketSize(Count current, uint32_t i) const; + + // Return a string description of what goes in a given bucket. + // Most commonly this is the numeric value, but in derived classes it may + // be a name (or string description) given to the bucket. + virtual const std::string GetAsciiBucketRange(uint32_t it) const; + + private: + // Allow tests to corrupt our innards for testing purposes. + friend class HistogramTest; + FRIEND_TEST_ALL_PREFIXES(HistogramTest, BoundsTest); + FRIEND_TEST_ALL_PREFIXES(HistogramTest, BucketPlacementTest); + FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptSampleCounts); + + friend class StatisticsRecorder; // To allow it to delete duplicates. + friend class StatisticsRecorderTest; + + friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo( + base::PickleIterator* iter); + static HistogramBase* DeserializeInfoImpl(base::PickleIterator* iter); + + // Create a snapshot containing all samples (both logged and unlogged). + // Implementation of SnapshotSamples method with a more specific type for + // internal use. + std::unique_ptr SnapshotAllSamples() const; + + // Create a copy of unlogged samples. + std::unique_ptr SnapshotUnloggedSamples() const; + + //---------------------------------------------------------------------------- + // Helpers for emitting Ascii graphic. Each method appends data to output. + + void WriteAsciiImpl(bool graph_it, + const std::string& newline, + std::string* output) const; + + // Find out how large (graphically) the largest bucket will appear to be. + double GetPeakBucketSize(const SampleVectorBase& samples) const; + + // Write a common header message describing this histogram. + void WriteAsciiHeader(const SampleVectorBase& samples, + Count sample_count, + std::string* output) const; + + // Write information about previous, current, and next buckets. + // Information such as cumulative percentage, etc. + void WriteAsciiBucketContext(const int64_t past, + const Count current, + const int64_t remaining, + const uint32_t i, + std::string* output) const; + + // WriteJSON calls these. + void GetParameters(DictionaryValue* params) const override; + + void GetCountAndBucketData(Count* count, + int64_t* sum, + ListValue* buckets) const override; + + // Samples that have not yet been logged with SnapshotDelta(). + std::unique_ptr unlogged_samples_; + + // Accumulation of all samples that have been logged with SnapshotDelta(). + std::unique_ptr logged_samples_; + +#if DCHECK_IS_ON() // Don't waste memory if it won't be used. + // Flag to indicate if PrepareFinalDelta has been previously called. It is + // used to DCHECK that a final delta is not created multiple times. + mutable bool final_delta_created_ = false; +#endif + + DISALLOW_COPY_AND_ASSIGN(Histogram); +}; + +//------------------------------------------------------------------------------ + +// LinearHistogram is a more traditional histogram, with evenly spaced +// buckets. +class BASE_EXPORT LinearHistogram : public Histogram { + public: + ~LinearHistogram() override; + + /* minimum should start from 1. 0 is as minimum is invalid. 0 is an implicit + default underflow bucket. */ + static HistogramBase* FactoryGet(const std::string& name, + Sample minimum, + Sample maximum, + uint32_t bucket_count, + int32_t flags); + static HistogramBase* FactoryTimeGet(const std::string& name, + TimeDelta minimum, + TimeDelta maximum, + uint32_t bucket_count, + int32_t flags); + + // Overloads of the above two functions that take a const char* |name| param, + // to avoid code bloat from the std::string constructor being inlined into + // call sites. + static HistogramBase* FactoryGet(const char* name, + Sample minimum, + Sample maximum, + uint32_t bucket_count, + int32_t flags); + static HistogramBase* FactoryTimeGet(const char* name, + TimeDelta minimum, + TimeDelta maximum, + uint32_t bucket_count, + int32_t flags); + + // Create a histogram using data in persistent storage. + static std::unique_ptr PersistentCreate( + const char* name, + Sample minimum, + Sample maximum, + const BucketRanges* ranges, + const DelayedPersistentAllocation& counts, + const DelayedPersistentAllocation& logged_counts, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta); + + struct DescriptionPair { + Sample sample; + const char* description; // Null means end of a list of pairs. + }; + + // Create a LinearHistogram and store a list of number/text values for use in + // writing the histogram graph. + // |descriptions| can be NULL, which means no special descriptions to set. If + // it's not NULL, the last element in the array must has a NULL in its + // "description" field. + static HistogramBase* FactoryGetWithRangeDescription( + const std::string& name, + Sample minimum, + Sample maximum, + uint32_t bucket_count, + int32_t flags, + const DescriptionPair descriptions[]); + + static void InitializeBucketRanges(Sample minimum, + Sample maximum, + BucketRanges* ranges); + + // Overridden from Histogram: + HistogramType GetHistogramType() const override; + + protected: + class Factory; + + LinearHistogram(const char* name, + Sample minimum, + Sample maximum, + const BucketRanges* ranges); + + LinearHistogram(const char* name, + Sample minimum, + Sample maximum, + const BucketRanges* ranges, + const DelayedPersistentAllocation& counts, + const DelayedPersistentAllocation& logged_counts, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta); + + double GetBucketSize(Count current, uint32_t i) const override; + + // If we have a description for a bucket, then return that. Otherwise + // let parent class provide a (numeric) description. + const std::string GetAsciiBucketRange(uint32_t i) const override; + + // Skip printing of name for numeric range if we have a name (and if this is + // an empty bucket). + bool PrintEmptyBucket(uint32_t index) const override; + + private: + friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo( + base::PickleIterator* iter); + static HistogramBase* DeserializeInfoImpl(base::PickleIterator* iter); + + // For some ranges, we store a printable description of a bucket range. + // If there is no description, then GetAsciiBucketRange() uses parent class + // to provide a description. + typedef std::map BucketDescriptionMap; + BucketDescriptionMap bucket_description_; + + DISALLOW_COPY_AND_ASSIGN(LinearHistogram); +}; + +//------------------------------------------------------------------------------ + +// ScaledLinearHistogram is a wrapper around a linear histogram that scales the +// counts down by some factor. Remainder values are kept locally but lost when +// uploaded or serialized. The integral counts are rounded up/down so should +// average to the correct value when many reports are added. +// +// This is most useful when adding many counts at once via AddCount() that can +// cause overflows of the 31-bit counters, usually with an enum as the value. +class BASE_EXPORT ScaledLinearHistogram { + using AtomicCount = Histogram::AtomicCount; + using Sample = Histogram::Sample; + + public: + // Currently only works with "exact" linear histograms: minimum=1, maximum=N, + // and bucket_count=N+1. + ScaledLinearHistogram(const char* name, + Sample minimum, + Sample maximum, + uint32_t bucket_count, + int32_t scale, + int32_t flags); + + ~ScaledLinearHistogram(); + + // Like AddCount() but actually accumulates |count|/|scale| and increments + // the accumulated remainder by |count|%|scale|. An additional increment + // is done when the remainder has grown sufficiently large. + void AddScaledCount(Sample value, int count); + + int32_t scale() const { return scale_; } + LinearHistogram* histogram() { return histogram_; } + + private: + // Pointer to the underlying histogram. Ownership of it remains with + // the statistics-recorder. + LinearHistogram* const histogram_; + + // The scale factor of the sample counts. + const int32_t scale_; + + // A vector of "remainder" counts indexed by bucket number. These values + // may be negative as the scaled count is actually bumped once the + // remainder is 1/2 way to the scale value (thus "rounding"). + std::vector remainders_; + + DISALLOW_COPY_AND_ASSIGN(ScaledLinearHistogram); +}; + +//------------------------------------------------------------------------------ + +// BooleanHistogram is a histogram for booleans. +class BASE_EXPORT BooleanHistogram : public LinearHistogram { + public: + static HistogramBase* FactoryGet(const std::string& name, int32_t flags); + + // Overload of the above function that takes a const char* |name| param, + // to avoid code bloat from the std::string constructor being inlined into + // call sites. + static HistogramBase* FactoryGet(const char* name, int32_t flags); + + // Create a histogram using data in persistent storage. + static std::unique_ptr PersistentCreate( + const char* name, + const BucketRanges* ranges, + const DelayedPersistentAllocation& counts, + const DelayedPersistentAllocation& logged_counts, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta); + + HistogramType GetHistogramType() const override; + + protected: + class Factory; + + private: + BooleanHistogram(const char* name, const BucketRanges* ranges); + BooleanHistogram(const char* name, + const BucketRanges* ranges, + const DelayedPersistentAllocation& counts, + const DelayedPersistentAllocation& logged_counts, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta); + + friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo( + base::PickleIterator* iter); + static HistogramBase* DeserializeInfoImpl(base::PickleIterator* iter); + + DISALLOW_COPY_AND_ASSIGN(BooleanHistogram); +}; + +//------------------------------------------------------------------------------ + +// CustomHistogram is a histogram for a set of custom integers. +class BASE_EXPORT CustomHistogram : public Histogram { + public: + // |custom_ranges| contains a vector of limits on ranges. Each limit should be + // > 0 and < kSampleType_MAX. (Currently 0 is still accepted for backward + // compatibility). The limits can be unordered or contain duplication, but + // client should not depend on this. + static HistogramBase* FactoryGet(const std::string& name, + const std::vector& custom_ranges, + int32_t flags); + + // Overload of the above function that takes a const char* |name| param, + // to avoid code bloat from the std::string constructor being inlined into + // call sites. + static HistogramBase* FactoryGet(const char* name, + const std::vector& custom_ranges, + int32_t flags); + + // Create a histogram using data in persistent storage. + static std::unique_ptr PersistentCreate( + const char* name, + const BucketRanges* ranges, + const DelayedPersistentAllocation& counts, + const DelayedPersistentAllocation& logged_counts, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta); + + // Overridden from Histogram: + HistogramType GetHistogramType() const override; + + // Helper method for transforming an array of valid enumeration values + // to the std::vector expected by UMA_HISTOGRAM_CUSTOM_ENUMERATION. + // This function ensures that a guard bucket exists right after any + // valid sample value (unless the next higher sample is also a valid value), + // so that invalid samples never fall into the same bucket as valid samples. + static std::vector ArrayToCustomEnumRanges( + base::span values); + + protected: + class Factory; + + CustomHistogram(const char* name, const BucketRanges* ranges); + + CustomHistogram(const char* name, + const BucketRanges* ranges, + const DelayedPersistentAllocation& counts, + const DelayedPersistentAllocation& logged_counts, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta); + + // HistogramBase implementation: + void SerializeInfoImpl(base::Pickle* pickle) const override; + + double GetBucketSize(Count current, uint32_t i) const override; + + private: + friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo( + base::PickleIterator* iter); + static HistogramBase* DeserializeInfoImpl(base::PickleIterator* iter); + + static bool ValidateCustomRanges(const std::vector& custom_ranges); + + DISALLOW_COPY_AND_ASSIGN(CustomHistogram); +}; + +} // namespace base + +#endif // BASE_METRICS_HISTOGRAM_H_ diff --git a/native/zucchini/vendor/libchrome/base/metrics/histogram_base.h b/native/zucchini/vendor/libchrome/base/metrics/histogram_base.h new file mode 100644 index 0000000..f128ff2 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/metrics/histogram_base.h @@ -0,0 +1,307 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_METRICS_HISTOGRAM_BASE_H_ +#define BASE_METRICS_HISTOGRAM_BASE_H_ + +#include +#include +#include + +#include +#include +#include + +#include "base/atomicops.h" +#include "base/base_export.h" +#include "base/macros.h" +#include "base/strings/string_piece.h" +#include "base/time/time.h" + +namespace base { + +class DictionaryValue; +class HistogramBase; +class HistogramSamples; +class ListValue; +class Pickle; +class PickleIterator; + +//////////////////////////////////////////////////////////////////////////////// +// This enum is used to facilitate deserialization of histograms from other +// processes into the browser. If you create another class that inherits from +// HistogramBase, add new histogram types and names below. + +enum HistogramType { + HISTOGRAM, + LINEAR_HISTOGRAM, + BOOLEAN_HISTOGRAM, + CUSTOM_HISTOGRAM, + SPARSE_HISTOGRAM, + DUMMY_HISTOGRAM, +}; + +// Controls the verbosity of the information when the histogram is serialized to +// a JSON. +// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base.metrics +enum JSONVerbosityLevel { + // The histogram is completely serialized. + JSON_VERBOSITY_LEVEL_FULL, + // The bucket information is not serialized. + JSON_VERBOSITY_LEVEL_OMIT_BUCKETS, +}; + +std::string HistogramTypeToString(HistogramType type); + +// This enum is used for reporting how many histograms and of what types and +// variations are being created. It has to be in the main .h file so it is +// visible to files that define the various histogram types. +enum HistogramReport { + // Count the number of reports created. The other counts divided by this + // number will give the average per run of the program. + HISTOGRAM_REPORT_CREATED = 0, + + // Count the total number of histograms created. It is the limit against + // which all others are compared. + HISTOGRAM_REPORT_HISTOGRAM_CREATED = 1, + + // Count the total number of histograms looked-up. It's better to cache + // the result of a single lookup rather than do it repeatedly. + HISTOGRAM_REPORT_HISTOGRAM_LOOKUP = 2, + + // These count the individual histogram types. This must follow the order + // of HistogramType above. + HISTOGRAM_REPORT_TYPE_LOGARITHMIC = 3, + HISTOGRAM_REPORT_TYPE_LINEAR = 4, + HISTOGRAM_REPORT_TYPE_BOOLEAN = 5, + HISTOGRAM_REPORT_TYPE_CUSTOM = 6, + HISTOGRAM_REPORT_TYPE_SPARSE = 7, + + // These indicate the individual flags that were set. + HISTOGRAM_REPORT_FLAG_UMA_TARGETED = 8, + HISTOGRAM_REPORT_FLAG_UMA_STABILITY = 9, + HISTOGRAM_REPORT_FLAG_PERSISTENT = 10, + + // This must be last. + HISTOGRAM_REPORT_MAX = 11 +}; + +// Create or find existing histogram that matches the pickled info. +// Returns NULL if the pickled data has problems. +BASE_EXPORT HistogramBase* DeserializeHistogramInfo(base::PickleIterator* iter); + +//////////////////////////////////////////////////////////////////////////////// + +class BASE_EXPORT HistogramBase { + public: + typedef int32_t Sample; // Used for samples. + typedef subtle::Atomic32 AtomicCount; // Used to count samples. + typedef int32_t Count; // Used to manipulate counts in temporaries. + + static const Sample kSampleType_MAX; // INT_MAX + + enum Flags { + kNoFlags = 0x0, + + // Histogram should be UMA uploaded. + kUmaTargetedHistogramFlag = 0x1, + + // Indicates that this is a stability histogram. This flag exists to specify + // which histograms should be included in the initial stability log. Please + // refer to |MetricsService::PrepareInitialStabilityLog|. + kUmaStabilityHistogramFlag = kUmaTargetedHistogramFlag | 0x2, + + // Indicates that the histogram was pickled to be sent across an IPC + // Channel. If we observe this flag on a histogram being aggregated into + // after IPC, then we are running in a single process mode, and the + // aggregation should not take place (as we would be aggregating back into + // the source histogram!). + kIPCSerializationSourceFlag = 0x10, + + // Indicates that a callback exists for when a new sample is recorded on + // this histogram. We store this as a flag with the histogram since + // histograms can be in performance critical code, and this allows us + // to shortcut looking up the callback if it doesn't exist. + kCallbackExists = 0x20, + + // Indicates that the histogram is held in "persistent" memory and may + // be accessible between processes. This is only possible if such a + // memory segment has been created/attached, used to create a Persistent- + // MemoryAllocator, and that loaded into the Histogram module before this + // histogram is created. + kIsPersistent = 0x40, + }; + + // Histogram data inconsistency types. + enum Inconsistency : uint32_t { + NO_INCONSISTENCIES = 0x0, + RANGE_CHECKSUM_ERROR = 0x1, + BUCKET_ORDER_ERROR = 0x2, + COUNT_HIGH_ERROR = 0x4, + COUNT_LOW_ERROR = 0x8, + + NEVER_EXCEEDED_VALUE = 0x10, + }; + + // Construct the base histogram. The name is not copied; it's up to the + // caller to ensure that it lives at least as long as this object. + explicit HistogramBase(const char* name); + virtual ~HistogramBase(); + + const char* histogram_name() const { return histogram_name_; } + + // Compares |name| to the histogram name and triggers a DCHECK if they do not + // match. This is a helper function used by histogram macros, which results in + // in more compact machine code being generated by the macros. + virtual void CheckName(const StringPiece& name) const; + + // Get a unique ID for this histogram's samples. + virtual uint64_t name_hash() const = 0; + + // Operations with Flags enum. + int32_t flags() const { return subtle::NoBarrier_Load(&flags_); } + void SetFlags(int32_t flags); + void ClearFlags(int32_t flags); + + virtual HistogramType GetHistogramType() const = 0; + + // Whether the histogram has construction arguments as parameters specified. + // For histograms that don't have the concept of minimum, maximum or + // bucket_count, this function always returns false. + virtual bool HasConstructionArguments( + Sample expected_minimum, + Sample expected_maximum, + uint32_t expected_bucket_count) const = 0; + + virtual void Add(Sample value) = 0; + + // In Add function the |value| bucket is increased by one, but in some use + // cases we need to increase this value by an arbitrary integer. AddCount + // function increases the |value| bucket by |count|. |count| should be greater + // than or equal to 1. + virtual void AddCount(Sample value, int count) = 0; + + // Similar to above but divides |count| by the |scale| amount. Probabilistic + // rounding is used to yield a reasonably accurate total when many samples + // are added. Methods for common cases of scales 1000 and 1024 are included. + // The ScaledLinearHistogram (which can also used be for enumerations) may be + // a better (and faster) solution. + void AddScaled(Sample value, int count, int scale); + void AddKilo(Sample value, int count); // scale=1000 + void AddKiB(Sample value, int count); // scale=1024 + + // Convenient functions that call Add(Sample). + void AddTime(const TimeDelta& time) { AddTimeMillisecondsGranularity(time); } + void AddTimeMillisecondsGranularity(const TimeDelta& time); + // Note: AddTimeMicrosecondsGranularity() drops the report if this client + // doesn't have a high-resolution clock. + void AddTimeMicrosecondsGranularity(const TimeDelta& time); + void AddBoolean(bool value); + + virtual void AddSamples(const HistogramSamples& samples) = 0; + virtual bool AddSamplesFromPickle(base::PickleIterator* iter) = 0; + + // Serialize the histogram info into |pickle|. + // Note: This only serializes the construction arguments of the histogram, but + // does not serialize the samples. + void SerializeInfo(base::Pickle* pickle) const; + + // Try to find out data corruption from histogram and the samples. + // The returned value is a combination of Inconsistency enum. + virtual uint32_t FindCorruption(const HistogramSamples& samples) const; + + // Snapshot the current complete set of sample data. + // Override with atomic/locked snapshot if needed. + // NOTE: this data can overflow for long-running sessions. It should be + // handled with care and this method is recommended to be used only + // in about:histograms and test code. + virtual std::unique_ptr SnapshotSamples() const = 0; + + // Calculate the change (delta) in histogram counts since the previous call + // to this method. Each successive call will return only those counts + // changed since the last call. + virtual std::unique_ptr SnapshotDelta() = 0; + + // Calculate the change (delta) in histogram counts since the previous call + // to SnapshotDelta() but do so without modifying any internal data as to + // what was previous logged. After such a call, no further calls to this + // method or to SnapshotDelta() should be done as the result would include + // data previously returned. Because no internal data is changed, this call + // can be made on "const" histograms such as those with data held in + // read-only memory. + virtual std::unique_ptr SnapshotFinalDelta() const = 0; + + // The following methods provide graphical histogram displays. + virtual void WriteHTMLGraph(std::string* output) const = 0; + virtual void WriteAscii(std::string* output) const = 0; + + // TODO(bcwhite): Remove this after https://crbug/836875. + virtual void ValidateHistogramContents() const; + + // Produce a JSON representation of the histogram with |verbosity_level| as + // the serialization verbosity. This is implemented with the help of + // GetParameters and GetCountAndBucketData; overwrite them to customize the + // output. + void WriteJSON(std::string* output, JSONVerbosityLevel verbosity_level) const; + + protected: + enum ReportActivity { HISTOGRAM_CREATED, HISTOGRAM_LOOKUP }; + + // Subclasses should implement this function to make SerializeInfo work. + virtual void SerializeInfoImpl(base::Pickle* pickle) const = 0; + + // Writes information about the construction parameters in |params|. + virtual void GetParameters(DictionaryValue* params) const = 0; + + // Writes information about the current (non-empty) buckets and their sample + // counts to |buckets|, the total sample count to |count| and the total sum + // to |sum|. + virtual void GetCountAndBucketData(Count* count, + int64_t* sum, + ListValue* buckets) const = 0; + + //// Produce actual graph (set of blank vs non blank char's) for a bucket. + void WriteAsciiBucketGraph(double current_size, + double max_size, + std::string* output) const; + + // Return a string description of what goes in a given bucket. + const std::string GetSimpleAsciiBucketRange(Sample sample) const; + + // Write textual description of the bucket contents (relative to histogram). + // Output is the count in the buckets, as well as the percentage. + void WriteAsciiBucketValue(Count current, + double scaled_sum, + std::string* output) const; + + // Retrieves the callback for this histogram, if one exists, and runs it + // passing |sample| as the parameter. + void FindAndRunCallback(Sample sample) const; + + // Gets a permanent string that can be used for histogram objects when the + // original is not a code constant or held in persistent memory. + static const char* GetPermanentName(const std::string& name); + + private: + friend class HistogramBaseTest; + + // A pointer to permanent storage where the histogram name is held. This can + // be code space or the output of GetPermanentName() or any other storage + // that is known to never change. This is not StringPiece because (a) char* + // is 1/2 the size and (b) StringPiece transparently casts from std::string + // which can easily lead to a pointer to non-permanent space. + // For persistent histograms, this will simply point into the persistent + // memory segment, thus avoiding duplication. For heap histograms, the + // GetPermanentName method will create the necessary copy. + const char* const histogram_name_; + + // Additional information about the histogram. + AtomicCount flags_; + + DISALLOW_COPY_AND_ASSIGN(HistogramBase); +}; + +} // namespace base + +#endif // BASE_METRICS_HISTOGRAM_BASE_H_ diff --git a/native/zucchini/vendor/libchrome/base/metrics/histogram_functions.h b/native/zucchini/vendor/libchrome/base/metrics/histogram_functions.h new file mode 100644 index 0000000..60c0057 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/metrics/histogram_functions.h @@ -0,0 +1,158 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_METRICS_HISTOGRAM_FUNCTIONS_H_ +#define BASE_METRICS_HISTOGRAM_FUNCTIONS_H_ + +#include "base/metrics/histogram.h" +#include "base/metrics/histogram_base.h" +#include "base/time/time.h" + +// Functions for recording metrics. +// +// For best practices on deciding when to emit to a histogram and what form +// the histogram should take, see +// https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/histograms/README.md + +// Functions for recording UMA histograms. These can be used for cases +// when the histogram name is generated at runtime. The functionality is +// equivalent to macros defined in histogram_macros.h but allowing non-constant +// histogram names. These functions are slower compared to their macro +// equivalent because the histogram objects are not cached between calls. +// So, these shouldn't be used in performance critical code. +namespace base { + +// For histograms with linear buckets. +// Used for capturing integer data with a linear bucketing scheme. This can be +// used when you want the exact value of some small numeric count, with a max of +// 100 or less. If you need to capture a range of greater than 100, we recommend +// the use of the COUNT histograms below. +// Sample usage: +// base::UmaHistogramExactLinear("Histogram.Linear", some_value, 10); +BASE_EXPORT void UmaHistogramExactLinear(const std::string& name, + int sample, + int value_max); + +// For adding a sample to an enumerated histogram. +// Sample usage: +// // These values are persisted to logs. Entries should not be renumbered and +// // numeric values should never be reused. +// enum class MyEnum { +// FIRST_VALUE = 0, +// SECOND_VALUE = 1, +// ... +// FINAL_VALUE = N, +// COUNT +// }; +// base::UmaHistogramEnumeration("My.Enumeration", +// MyEnum::SOME_VALUE, MyEnum::COUNT); +// +// Note: The value in |sample| must be strictly less than |enum_size|. +template +void UmaHistogramEnumeration(const std::string& name, T sample, T enum_size) { + static_assert(std::is_enum::value, + "Non enum passed to UmaHistogramEnumeration"); + DCHECK_LE(static_cast(enum_size), static_cast(INT_MAX)); + DCHECK_LT(static_cast(sample), static_cast(enum_size)); + return UmaHistogramExactLinear(name, static_cast(sample), + static_cast(enum_size)); +} + +// Same as above, but uses T::kMaxValue as the inclusive maximum value of the +// enum. +template +void UmaHistogramEnumeration(const std::string& name, T sample) { + static_assert(std::is_enum::value, + "Non enum passed to UmaHistogramEnumeration"); + DCHECK_LE(static_cast(T::kMaxValue), + static_cast(INT_MAX) - 1); + DCHECK_LE(static_cast(sample), + static_cast(T::kMaxValue)); + return UmaHistogramExactLinear(name, static_cast(sample), + static_cast(T::kMaxValue) + 1); +} + +// For adding boolean sample to histogram. +// Sample usage: +// base::UmaHistogramBoolean("My.Boolean", true) +BASE_EXPORT void UmaHistogramBoolean(const std::string& name, bool sample); + +// For adding histogram with percent. +// Percents are integer between 1 and 100. +// Sample usage: +// base::UmaHistogramPercentage("My.Percent", 69) +BASE_EXPORT void UmaHistogramPercentage(const std::string& name, int percent); + +// For adding counts histogram. +// Sample usage: +// base::UmaHistogramCustomCounts("My.Counts", some_value, 1, 600, 30) +BASE_EXPORT void UmaHistogramCustomCounts(const std::string& name, + int sample, + int min, + int max, + int buckets); + +// Counts specialization for maximum counts 100, 1000, 10k, 100k, 1M and 10M. +BASE_EXPORT void UmaHistogramCounts100(const std::string& name, int sample); +BASE_EXPORT void UmaHistogramCounts1000(const std::string& name, int sample); +BASE_EXPORT void UmaHistogramCounts10000(const std::string& name, int sample); +BASE_EXPORT void UmaHistogramCounts100000(const std::string& name, int sample); +BASE_EXPORT void UmaHistogramCounts1M(const std::string& name, int sample); +BASE_EXPORT void UmaHistogramCounts10M(const std::string& name, int sample); + +// For histograms storing times. +BASE_EXPORT void UmaHistogramCustomTimes(const std::string& name, + TimeDelta sample, + TimeDelta min, + TimeDelta max, + int buckets); +// For short timings from 1 ms up to 10 seconds (50 buckets). +BASE_EXPORT void UmaHistogramTimes(const std::string& name, TimeDelta sample); +// For medium timings up to 3 minutes (50 buckets). +BASE_EXPORT void UmaHistogramMediumTimes(const std::string& name, + TimeDelta sample); +// For time intervals up to 1 hr (50 buckets). +BASE_EXPORT void UmaHistogramLongTimes(const std::string& name, + TimeDelta sample); + +// For recording memory related histograms. +// Used to measure common KB-granularity memory stats. Range is up to 500M. +BASE_EXPORT void UmaHistogramMemoryKB(const std::string& name, int sample); +// Used to measure common MB-granularity memory stats. Range is up to ~1G. +BASE_EXPORT void UmaHistogramMemoryMB(const std::string& name, int sample); +// Used to measure common MB-granularity memory stats. Range is up to ~64G. +BASE_EXPORT void UmaHistogramMemoryLargeMB(const std::string& name, int sample); + +// For recording sparse histograms. +// The |sample| can be a negative or non-negative number. +// +// Sparse histograms are well suited for recording counts of exact sample values +// that are sparsely distributed over a relatively large range, in cases where +// ultra-fast performance is not critical. For instance, Sqlite.Version.* are +// sparse because for any given database, there's going to be exactly one +// version logged. +// +// Performance: +// ------------ +// Sparse histograms are typically more memory-efficient but less time-efficient +// than other histograms. Essentially, they sparse histograms use a map rather +// than a vector for their backing storage; they also require lock acquisition +// to increment a sample, whereas other histogram do not. Hence, each increment +// operation is a bit slower than for other histograms. But, if the data is +// sparse, then they use less memory client-side, because they allocate buckets +// on demand rather than preallocating. +// +// Data size: +// ---------- +// Note that server-side, we still need to load all buckets, across all users, +// at once. Thus, please avoid exploding such histograms, i.e. uploading many +// many distinct values to the server (across all users). Concretely, keep the +// number of distinct values <= 100 ideally, definitely <= 1000. If you have no +// guarantees on the range of your data, use clamping, e.g.: +// UmaHistogramSparse("MyHistogram", ClampToRange(value, 0, 200)); +BASE_EXPORT void UmaHistogramSparse(const std::string& name, int sample); + +} // namespace base + +#endif // BASE_METRICS_HISTOGRAM_FUNCTIONS_H_ diff --git a/native/zucchini/vendor/libchrome/base/metrics/histogram_macros.h b/native/zucchini/vendor/libchrome/base/metrics/histogram_macros.h new file mode 100644 index 0000000..93bd4bd --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/metrics/histogram_macros.h @@ -0,0 +1,382 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_METRICS_HISTOGRAM_MACROS_H_ +#define BASE_METRICS_HISTOGRAM_MACROS_H_ + +#include "base/macros.h" +#include "base/metrics/histogram.h" +#include "base/metrics/histogram_macros_internal.h" +#include "base/metrics/histogram_macros_local.h" +#include "base/time/time.h" + + +// Macros for efficient use of histograms. +// +// For best practices on deciding when to emit to a histogram and what form +// the histogram should take, see +// https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/histograms/README.md + +// TODO(rkaplow): Link to proper documentation on metric creation once we have +// it in a good state. + +// All of these macros must be called with |name| as a runtime constant - it +// doesn't have to literally be a constant, but it must be the same string on +// all calls from a particular call site. If this rule is violated, it is +// possible the data will be written to the wrong histogram. + +//------------------------------------------------------------------------------ +// Enumeration histograms. + +// These macros create histograms for enumerated data. Ideally, the data should +// be of the form of "event occurs, log the result". We recommended not putting +// related but not directly connected data as enums within the same histogram. +// You should be defining an associated Enum, and the input sample should be +// an element of the Enum. +// All of these macros must be called with |name| as a runtime constant. + +// The first variant of UMA_HISTOGRAM_ENUMERATION accepts two arguments: the +// histogram name and the enum sample. It deduces the correct boundary value to +// use by looking for an enumerator with the name kMaxValue. kMaxValue should +// share the value of the highest enumerator: this avoids switch statements +// having to handle a sentinel no-op value. +// +// Sample usage: +// // These values are persisted to logs. Entries should not be renumbered and +// // numeric values should never be reused. +// enum class MyEnum { +// kFirstValue = 0, +// kSecondValue = 1, +// ... +// kFinalValue = N, +// kMaxValue = kFinalValue, +// }; +// UMA_HISTOGRAM_ENUMERATION("My.Enumeration", MyEnum::kSomeValue); +// +// The second variant requires three arguments: the first two are the same as +// before, and the third argument is the enum boundary: this must be strictly +// greater than any other enumerator that will be sampled. +// +// Sample usage: +// // These values are persisted to logs. Entries should not be renumbered and +// // numeric values should never be reused. +// enum class MyEnum { +// FIRST_VALUE = 0, +// SECOND_VALUE = 1, +// ... +// FINAL_VALUE = N, +// COUNT +// }; +// UMA_HISTOGRAM_ENUMERATION("My.Enumeration", +// MyEnum::SOME_VALUE, MyEnum::COUNT); +// +// Note: If the enum is used in a switch, it is often desirable to avoid writing +// a case statement to handle an unused sentinel value (i.e. COUNT in the above +// example). For scoped enums, this is awkward since it requires casting the +// enum to an arithmetic type and adding one. Instead, prefer the two argument +// version of the macro which automatically deduces the boundary from kMaxValue. +#define UMA_HISTOGRAM_ENUMERATION(name, ...) \ + CR_EXPAND_ARG(INTERNAL_UMA_HISTOGRAM_ENUMERATION_GET_MACRO( \ + __VA_ARGS__, INTERNAL_UMA_HISTOGRAM_ENUMERATION_SPECIFY_BOUNDARY, \ + INTERNAL_UMA_HISTOGRAM_ENUMERATION_DEDUCE_BOUNDARY)( \ + name, __VA_ARGS__, base::HistogramBase::kUmaTargetedHistogramFlag)) + +// As above but "scaled" count to avoid overflows caused by increments of +// large amounts. See UMA_HISTOGRAM_SCALED_EXACT_LINEAR for more information. +// Only the new format utilizing an internal kMaxValue is supported. +// It'll be necessary to #include "base/lazy_instance.h" to use this macro. +#define UMA_HISTOGRAM_SCALED_ENUMERATION(name, sample, count, scale) \ + INTERNAL_HISTOGRAM_SCALED_ENUMERATION_WITH_FLAG( \ + name, sample, count, scale, \ + base::HistogramBase::kUmaTargetedHistogramFlag) + +// Histogram for boolean values. + +// Sample usage: +// UMA_HISTOGRAM_BOOLEAN("Histogram.Boolean", bool); +#define UMA_HISTOGRAM_BOOLEAN(name, sample) \ + STATIC_HISTOGRAM_POINTER_BLOCK(name, AddBoolean(sample), \ + base::BooleanHistogram::FactoryGet(name, \ + base::HistogramBase::kUmaTargetedHistogramFlag)) + +//------------------------------------------------------------------------------ +// Linear histograms. + +// All of these macros must be called with |name| as a runtime constant. + +// Used for capturing integer data with a linear bucketing scheme. This can be +// used when you want the exact value of some small numeric count, with a max of +// 100 or less. If you need to capture a range of greater than 100, we recommend +// the use of the COUNT histograms below. + +// Sample usage: +// UMA_HISTOGRAM_EXACT_LINEAR("Histogram.Linear", count, 10); +#define UMA_HISTOGRAM_EXACT_LINEAR(name, sample, value_max) \ + INTERNAL_HISTOGRAM_EXACT_LINEAR_WITH_FLAG( \ + name, sample, value_max, base::HistogramBase::kUmaTargetedHistogramFlag) + +// Used for capturing basic percentages. This will be 100 buckets of size 1. + +// Sample usage: +// UMA_HISTOGRAM_PERCENTAGE("Histogram.Percent", percent_as_int); +#define UMA_HISTOGRAM_PERCENTAGE(name, percent_as_int) \ + UMA_HISTOGRAM_EXACT_LINEAR(name, percent_as_int, 101) + +//------------------------------------------------------------------------------ +// Scaled Linear histograms. + +// These take |count| and |scale| parameters to allow cumulative reporting of +// large numbers. Only the scaled count is reported but the reminder is kept so +// multiple calls will accumulate correctly. Only "exact linear" is supported. +// It'll be necessary to #include "base/lazy_instance.h" to use this macro. + +#define UMA_HISTOGRAM_SCALED_EXACT_LINEAR(name, sample, count, value_max, \ + scale) \ + INTERNAL_HISTOGRAM_SCALED_EXACT_LINEAR_WITH_FLAG( \ + name, sample, count, value_max, scale, \ + base::HistogramBase::kUmaTargetedHistogramFlag) + +//------------------------------------------------------------------------------ +// Count histograms. These are used for collecting numeric data. Note that we +// have macros for more specialized use cases below (memory, time, percentages). + +// The number suffixes here refer to the max size of the sample, i.e. COUNT_1000 +// will be able to collect samples of counts up to 1000. The default number of +// buckets in all default macros is 50. We recommend erring on the side of too +// large a range versus too short a range. +// These macros default to exponential histograms - i.e. the lengths of the +// bucket ranges exponentially increase as the sample range increases. +// These should *not* be used if you are interested in exact counts, i.e. a +// bucket range of 1. In these cases, you should use the ENUMERATION macros +// defined later. These should also not be used to capture the number of some +// event, i.e. "button X was clicked N times". In this cases, an enum should be +// used, ideally with an appropriate baseline enum entry included. +// All of these macros must be called with |name| as a runtime constant. + +// Sample usage: +// UMA_HISTOGRAM_COUNTS_1M("My.Histogram", sample); + +#define UMA_HISTOGRAM_COUNTS_100(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ + name, sample, 1, 100, 50) + +#define UMA_HISTOGRAM_COUNTS_1000(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ + name, sample, 1, 1000, 50) + +#define UMA_HISTOGRAM_COUNTS_10000(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ + name, sample, 1, 10000, 50) + +#define UMA_HISTOGRAM_COUNTS_100000(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ + name, sample, 1, 100000, 50) + +#define UMA_HISTOGRAM_COUNTS_1M(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ + name, sample, 1, 1000000, 50) + +#define UMA_HISTOGRAM_COUNTS_10M(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ + name, sample, 1, 10000000, 50) + +// This can be used when the default ranges are not sufficient. This macro lets +// the metric developer customize the min and max of the sampled range, as well +// as the number of buckets recorded. +// Any data outside the range here will be put in underflow and overflow +// buckets. Min values should be >=1 as emitted 0s will still go into the +// underflow bucket. + +// Sample usage: +// UMA_HISTOGRAM_CUSTOM_COUNTS("My.Histogram", 1, 100000000, 100); +#define UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \ + INTERNAL_HISTOGRAM_CUSTOM_COUNTS_WITH_FLAG( \ + name, sample, min, max, bucket_count, \ + base::HistogramBase::kUmaTargetedHistogramFlag) + +//------------------------------------------------------------------------------ +// Timing histograms. These are used for collecting timing data (generally +// latencies). + +// These macros create exponentially sized histograms (lengths of the bucket +// ranges exponentially increase as the sample range increases). The input +// sample is a base::TimeDelta. The output data is measured in ms granularity. +// All of these macros must be called with |name| as a runtime constant. + +// Sample usage: +// UMA_HISTOGRAM_TIMES("My.Timing.Histogram", time_delta); + +// Short timings - up to 10 seconds. For high-resolution (microseconds) timings, +// see UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES. +#define UMA_HISTOGRAM_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \ + name, sample, base::TimeDelta::FromMilliseconds(1), \ + base::TimeDelta::FromSeconds(10), 50) + +// Medium timings - up to 3 minutes. Note this starts at 10ms (no good reason, +// but not worth changing). +#define UMA_HISTOGRAM_MEDIUM_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \ + name, sample, base::TimeDelta::FromMilliseconds(10), \ + base::TimeDelta::FromMinutes(3), 50) + +// Long timings - up to an hour. +#define UMA_HISTOGRAM_LONG_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \ + name, sample, base::TimeDelta::FromMilliseconds(1), \ + base::TimeDelta::FromHours(1), 50) + +// Long timings with higher granularity - up to an hour with 100 buckets. +#define UMA_HISTOGRAM_LONG_TIMES_100(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \ + name, sample, base::TimeDelta::FromMilliseconds(1), \ + base::TimeDelta::FromHours(1), 100) + +// This can be used when the default ranges are not sufficient. This macro lets +// the metric developer customize the min and max of the sampled range, as well +// as the number of buckets recorded. + +// Sample usage: +// UMA_HISTOGRAM_CUSTOM_TIMES("Very.Long.Timing.Histogram", time_delta, +// base::TimeDelta::FromSeconds(1), base::TimeDelta::FromDays(1), 100); +#define UMA_HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \ + STATIC_HISTOGRAM_POINTER_BLOCK( \ + name, AddTimeMillisecondsGranularity(sample), \ + base::Histogram::FactoryTimeGet( \ + name, min, max, bucket_count, \ + base::HistogramBase::kUmaTargetedHistogramFlag)) + +// Same as UMA_HISTOGRAM_CUSTOM_TIMES but reports |sample| in microseconds, +// dropping the report if this client doesn't have a high-resolution clock. +// +// Note: dropping reports on clients with low-resolution clocks means these +// reports will be biased to a portion of the population on Windows. See +// Windows.HasHighResolutionTimeTicks for the affected sample. +// +// Sample usage: +// UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES( +// "High.Resolution.TimingMicroseconds.Histogram", time_delta, +// base::TimeDelta::FromMicroseconds(1), +// base::TimeDelta::FromMilliseconds(10), 100); +#define UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(name, sample, min, max, \ + bucket_count) \ + STATIC_HISTOGRAM_POINTER_BLOCK( \ + name, AddTimeMicrosecondsGranularity(sample), \ + base::Histogram::FactoryMicrosecondsTimeGet( \ + name, min, max, bucket_count, \ + base::HistogramBase::kUmaTargetedHistogramFlag)) + +// Scoped class which logs its time on this earth as a UMA statistic. This is +// recommended for when you want a histogram which measures the time it takes +// for a method to execute. This measures up to 10 seconds. It uses +// UMA_HISTOGRAM_TIMES under the hood. + +// Sample usage: +// void Function() { +// SCOPED_UMA_HISTOGRAM_TIMER("Component.FunctionTime"); +// ... +// } +#define SCOPED_UMA_HISTOGRAM_TIMER(name) \ + INTERNAL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(name, false, __COUNTER__) + +// Similar scoped histogram timer, but this uses UMA_HISTOGRAM_LONG_TIMES_100, +// which measures up to an hour, and uses 100 buckets. This is more expensive +// to store, so only use if this often takes >10 seconds. +#define SCOPED_UMA_HISTOGRAM_LONG_TIMER(name) \ + INTERNAL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(name, true, __COUNTER__) + + +//------------------------------------------------------------------------------ +// Memory histograms. + +// These macros create exponentially sized histograms (lengths of the bucket +// ranges exponentially increase as the sample range increases). The input +// sample must be a number measured in kilobytes. +// All of these macros must be called with |name| as a runtime constant. + +// Sample usage: +// UMA_HISTOGRAM_MEMORY_KB("My.Memory.Histogram", memory_in_kb); + +// Used to measure common KB-granularity memory stats. Range is up to 500000KB - +// approximately 500M. +#define UMA_HISTOGRAM_MEMORY_KB(name, sample) \ + UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1000, 500000, 50) + +// Used to measure common MB-granularity memory stats. Range is up to ~64G. +#define UMA_HISTOGRAM_MEMORY_LARGE_MB(name, sample) \ + UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 64000, 100) + + +//------------------------------------------------------------------------------ +// Stability-specific histograms. + +// Histograms logged in as stability histograms will be included in the initial +// stability log. See comments by declaration of +// MetricsService::PrepareInitialStabilityLog(). +// All of these macros must be called with |name| as a runtime constant. + +// For details on usage, see the documentation on the non-stability equivalents. + +#define UMA_STABILITY_HISTOGRAM_COUNTS_100(name, sample) \ + UMA_STABILITY_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 100, 50) + +#define UMA_STABILITY_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, \ + bucket_count) \ + INTERNAL_HISTOGRAM_CUSTOM_COUNTS_WITH_FLAG( \ + name, sample, min, max, bucket_count, \ + base::HistogramBase::kUmaStabilityHistogramFlag) + +#define UMA_STABILITY_HISTOGRAM_ENUMERATION(name, sample, enum_max) \ + INTERNAL_HISTOGRAM_ENUMERATION_WITH_FLAG( \ + name, sample, enum_max, \ + base::HistogramBase::kUmaStabilityHistogramFlag) + +//------------------------------------------------------------------------------ +// Histogram instantiation helpers. + +// Support a collection of histograms, perhaps one for each entry in an +// enumeration. This macro manages a block of pointers, adding to a specific +// one by its index. +// +// A typical instantiation looks something like this: +// STATIC_HISTOGRAM_POINTER_GROUP( +// GetHistogramNameForIndex(histogram_index), +// histogram_index, MAXIMUM_HISTOGRAM_INDEX, Add(some_delta), +// base::Histogram::FactoryGet( +// GetHistogramNameForIndex(histogram_index), +// MINIMUM_SAMPLE, MAXIMUM_SAMPLE, BUCKET_COUNT, +// base::HistogramBase::kUmaTargetedHistogramFlag)); +// +// Though it seems inefficient to generate the name twice, the first +// instance will be used only for DCHECK builds and the second will +// execute only during the first access to the given index, after which +// the pointer is cached and the name never needed again. +#define STATIC_HISTOGRAM_POINTER_GROUP(constant_histogram_name, index, \ + constant_maximum, \ + histogram_add_method_invocation, \ + histogram_factory_get_invocation) \ + do { \ + static base::subtle::AtomicWord atomic_histograms[constant_maximum]; \ + DCHECK_LE(0, index); \ + DCHECK_LT(index, constant_maximum); \ + HISTOGRAM_POINTER_USE(&atomic_histograms[index], constant_histogram_name, \ + histogram_add_method_invocation, \ + histogram_factory_get_invocation); \ + } while (0) + +//------------------------------------------------------------------------------ +// Deprecated histogram macros. Not recommended for current use. + +// Legacy name for UMA_HISTOGRAM_COUNTS_1M. Suggest using explicit naming +// and not using this macro going forward. +#define UMA_HISTOGRAM_COUNTS(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ + name, sample, 1, 1000000, 50) + +// MB-granularity memory metric. This has a short max (1G). +#define UMA_HISTOGRAM_MEMORY_MB(name, sample) \ + UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 1000, 50) + +// For an enum with customized range. In general, sparse histograms should be +// used instead. +// Samples should be one of the std::vector list provided via +// |custom_ranges|. See comments above CustomRanges::FactoryGet about the +// requirement of |custom_ranges|. You can use the helper function +// CustomHistogram::ArrayToCustomEnumRanges to transform a C-style array of +// valid sample values to a std::vector. +#define UMA_HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) \ + STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \ + base::CustomHistogram::FactoryGet(name, custom_ranges, \ + base::HistogramBase::kUmaTargetedHistogramFlag)) + +#endif // BASE_METRICS_HISTOGRAM_MACROS_H_ diff --git a/native/zucchini/vendor/libchrome/base/metrics/histogram_macros_internal.h b/native/zucchini/vendor/libchrome/base/metrics/histogram_macros_internal.h new file mode 100644 index 0000000..cc7c76a --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/metrics/histogram_macros_internal.h @@ -0,0 +1,268 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_METRICS_HISTOGRAM_MACROS_INTERNAL_H_ +#define BASE_METRICS_HISTOGRAM_MACROS_INTERNAL_H_ + +#include + +#include +#include + +#include "base/atomicops.h" +#include "base/logging.h" +#include "base/metrics/histogram.h" +#include "base/metrics/sparse_histogram.h" +#include "base/time/time.h" + +// This is for macros and helpers internal to base/metrics. They should not be +// used outside of this directory. For writing to UMA histograms, see +// histogram_macros.h. + +namespace base { +namespace internal { + +// Helper traits for deducing the boundary value for enums. +template +struct EnumSizeTraits { + static constexpr Enum Count() { + static_assert( + sizeof(Enum) == 0, + "enumerator must define kMaxValue enumerator to use this macro!"); + return Enum(); + } +}; + +// Since the UMA histogram macros expect a value one larger than the max defined +// enumerator value, add one. +template +struct EnumSizeTraits< + Enum, + std::enable_if_t::value>> { + static constexpr Enum Count() { + return static_cast( + static_cast>(Enum::kMaxValue) + 1); + } +}; + +} // namespace internal +} // namespace base + +// TODO(rkaplow): Improve commenting of these methods. +//------------------------------------------------------------------------------ +// Histograms are often put in areas where they are called many many times, and +// performance is critical. As a result, they are designed to have a very low +// recurring cost of executing (adding additional samples). Toward that end, +// the macros declare a static pointer to the histogram in question, and only +// take a "slow path" to construct (or find) the histogram on the first run +// through the macro. We leak the histograms at shutdown time so that we don't +// have to validate using the pointers at any time during the running of the +// process. + +// In some cases (integration into 3rd party code), it's useful to separate the +// definition of |atomic_histogram_pointer| from its use. To achieve this we +// define HISTOGRAM_POINTER_USE, which uses an |atomic_histogram_pointer|, and +// STATIC_HISTOGRAM_POINTER_BLOCK, which defines an |atomic_histogram_pointer| +// and forwards to HISTOGRAM_POINTER_USE. +#define HISTOGRAM_POINTER_USE(atomic_histogram_pointer, \ + constant_histogram_name, \ + histogram_add_method_invocation, \ + histogram_factory_get_invocation) \ + do { \ + /* \ + * Acquire_Load() ensures that we acquire visibility to the \ + * pointed-to data in the histogram. \ + */ \ + base::HistogramBase* histogram_pointer( \ + reinterpret_cast( \ + base::subtle::Acquire_Load(atomic_histogram_pointer))); \ + if (!histogram_pointer) { \ + /* \ + * This is the slow path, which will construct OR find the \ + * matching histogram. histogram_factory_get_invocation includes \ + * locks on a global histogram name map and is completely thread \ + * safe. \ + */ \ + histogram_pointer = histogram_factory_get_invocation; \ + \ + /* \ + * Use Release_Store to ensure that the histogram data is made \ + * available globally before we make the pointer visible. Several \ + * threads may perform this store, but the same value will be \ + * stored in all cases (for a given named/spec'ed histogram). \ + * We could do this without any barrier, since FactoryGet entered \ + * and exited a lock after construction, but this barrier makes \ + * things clear. \ + */ \ + base::subtle::Release_Store( \ + atomic_histogram_pointer, \ + reinterpret_cast(histogram_pointer)); \ + } \ + if (DCHECK_IS_ON()) \ + histogram_pointer->CheckName(constant_histogram_name); \ + histogram_pointer->histogram_add_method_invocation; \ + } while (0) + +// This is a helper macro used by other macros and shouldn't be used directly. +// Defines the static |atomic_histogram_pointer| and forwards to +// HISTOGRAM_POINTER_USE. +#define STATIC_HISTOGRAM_POINTER_BLOCK(constant_histogram_name, \ + histogram_add_method_invocation, \ + histogram_factory_get_invocation) \ + do { \ + /* \ + * The pointer's presence indicates that the initialization is complete. \ + * Initialization is idempotent, so it can safely be atomically repeated. \ + */ \ + static base::subtle::AtomicWord atomic_histogram_pointer = 0; \ + HISTOGRAM_POINTER_USE(&atomic_histogram_pointer, constant_histogram_name, \ + histogram_add_method_invocation, \ + histogram_factory_get_invocation); \ + } while (0) + +// This is a helper macro used by other macros and shouldn't be used directly. +#define INTERNAL_HISTOGRAM_CUSTOM_COUNTS_WITH_FLAG(name, sample, min, max, \ + bucket_count, flag) \ + STATIC_HISTOGRAM_POINTER_BLOCK( \ + name, Add(sample), \ + base::Histogram::FactoryGet(name, min, max, bucket_count, flag)) + +// This is a helper macro used by other macros and shouldn't be used directly. +// The bucketing scheme is linear with a bucket size of 1. For N items, +// recording values in the range [0, N - 1] creates a linear histogram with N + +// 1 buckets: +// [0, 1), [1, 2), ..., [N - 1, N) +// and an overflow bucket [N, infinity). +// +// Code should never emit to the overflow bucket; only to the other N buckets. +// This allows future versions of Chrome to safely increase the boundary size. +// Otherwise, the histogram would have [N - 1, infinity) as its overflow bucket, +// and so the maximal value (N - 1) would be emitted to this overflow bucket. +// But, if an additional value were later added, the bucket label for +// the value (N - 1) would change to [N - 1, N), which would result in different +// versions of Chrome using different bucket labels for identical data. +#define INTERNAL_HISTOGRAM_EXACT_LINEAR_WITH_FLAG(name, sample, boundary, \ + flag) \ + do { \ + static_assert(!std::is_enum::value, \ + "|sample| should not be an enum type!"); \ + static_assert(!std::is_enum::value, \ + "|boundary| should not be an enum type!"); \ + STATIC_HISTOGRAM_POINTER_BLOCK( \ + name, Add(sample), \ + base::LinearHistogram::FactoryGet(name, 1, boundary, boundary + 1, \ + flag)); \ + } while (0) + +// While this behaves the same as the above macro, the wrapping of a linear +// histogram with another object to do the scaling means the POINTER_BLOCK +// macro can't be used as it is tied to HistogramBase +#define INTERNAL_HISTOGRAM_SCALED_EXACT_LINEAR_WITH_FLAG( \ + name, sample, count, boundary, scale, flag) \ + do { \ + static_assert(!std::is_enum::value, \ + "|sample| should not be an enum type!"); \ + static_assert(!std::is_enum::value, \ + "|boundary| should not be an enum type!"); \ + class ScaledLinearHistogramInstance : public base::ScaledLinearHistogram { \ + public: \ + ScaledLinearHistogramInstance() \ + : ScaledLinearHistogram(name, \ + 1, \ + boundary, \ + boundary + 1, \ + scale, \ + flag) {} \ + }; \ + static base::LazyInstance::Leaky scaled; \ + scaled.Get().AddScaledCount(sample, count); \ + } while (0) + +// Helper for 'overloading' UMA_HISTOGRAM_ENUMERATION with a variable number of +// arguments. +#define INTERNAL_UMA_HISTOGRAM_ENUMERATION_GET_MACRO(_1, _2, NAME, ...) NAME + +#define INTERNAL_UMA_HISTOGRAM_ENUMERATION_DEDUCE_BOUNDARY(name, sample, \ + flags) \ + INTERNAL_HISTOGRAM_ENUMERATION_WITH_FLAG( \ + name, sample, base::internal::EnumSizeTraits::Count(), \ + flags) + +// Note: The value in |sample| must be strictly less than |enum_size|. +#define INTERNAL_UMA_HISTOGRAM_ENUMERATION_SPECIFY_BOUNDARY(name, sample, \ + enum_size, flags) \ + INTERNAL_HISTOGRAM_ENUMERATION_WITH_FLAG(name, sample, enum_size, flags) + +// Similar to the previous macro but intended for enumerations. This delegates +// the work to the previous macro, but supports scoped enumerations as well by +// forcing an explicit cast to the HistogramBase::Sample integral type. +// +// Note the range checks verify two separate issues: +// - that the declared enum size isn't out of range of HistogramBase::Sample +// - that the declared enum size is > 0 +// +// TODO(dcheng): This should assert that the passed in types are actually enum +// types. +#define INTERNAL_HISTOGRAM_ENUMERATION_WITH_FLAG(name, sample, boundary, flag) \ + do { \ + using decayed_sample = std::decay::type; \ + using decayed_boundary = std::decay::type; \ + static_assert(!std::is_enum::value || \ + std::is_enum::value, \ + "Unexpected: |boundary| is enum, but |sample| is not."); \ + static_assert(!std::is_enum::value || \ + !std::is_enum::value || \ + std::is_same::value, \ + "|sample| and |boundary| shouldn't be of different enums"); \ + static_assert( \ + static_cast(boundary) < \ + static_cast( \ + std::numeric_limits::max()), \ + "|boundary| is out of range of HistogramBase::Sample"); \ + INTERNAL_HISTOGRAM_EXACT_LINEAR_WITH_FLAG( \ + name, static_cast(sample), \ + static_cast(boundary), flag); \ + } while (0) + +#define INTERNAL_HISTOGRAM_SCALED_ENUMERATION_WITH_FLAG(name, sample, count, \ + scale, flag) \ + do { \ + using decayed_sample = std::decay::type; \ + static_assert(std::is_enum::value, \ + "Unexpected: |sample| is not at enum."); \ + constexpr auto boundary = \ + base::internal::EnumSizeTraits::Count(); \ + static_assert( \ + static_cast(boundary) < \ + static_cast( \ + std::numeric_limits::max()), \ + "|boundary| is out of range of HistogramBase::Sample"); \ + INTERNAL_HISTOGRAM_SCALED_EXACT_LINEAR_WITH_FLAG( \ + name, static_cast(sample), count, \ + static_cast(boundary), scale, flag); \ + } while (0) + +// This is a helper macro used by other macros and shouldn't be used directly. +// This is necessary to expand __COUNTER__ to an actual value. +#define INTERNAL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(name, is_long, key) \ + INTERNAL_SCOPED_UMA_HISTOGRAM_TIMER_UNIQUE(name, is_long, key) + +// This is a helper macro used by other macros and shouldn't be used directly. +#define INTERNAL_SCOPED_UMA_HISTOGRAM_TIMER_UNIQUE(name, is_long, key) \ + class ScopedHistogramTimer##key { \ + public: \ + ScopedHistogramTimer##key() : constructed_(base::TimeTicks::Now()) {} \ + ~ScopedHistogramTimer##key() { \ + base::TimeDelta elapsed = base::TimeTicks::Now() - constructed_; \ + if (is_long) { \ + UMA_HISTOGRAM_LONG_TIMES_100(name, elapsed); \ + } else { \ + UMA_HISTOGRAM_TIMES(name, elapsed); \ + } \ + } \ + private: \ + base::TimeTicks constructed_; \ + } scoped_histogram_timer_##key + +#endif // BASE_METRICS_HISTOGRAM_MACROS_INTERNAL_H_ diff --git a/native/zucchini/vendor/libchrome/base/metrics/histogram_macros_local.h b/native/zucchini/vendor/libchrome/base/metrics/histogram_macros_local.h new file mode 100644 index 0000000..c4d333b --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/metrics/histogram_macros_local.h @@ -0,0 +1,90 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_METRICS_HISTOGRAM_MACROS_LOCAL_H_ +#define BASE_METRICS_HISTOGRAM_MACROS_LOCAL_H_ + +#include "base/logging.h" +#include "base/metrics/histogram.h" +#include "base/metrics/histogram_macros_internal.h" +#include "base/time/time.h" + +// TODO(rkaplow): Migrate all LOCAL_* usage within Chromium to include this +// file instead of the histogram_macros.h file. + +//------------------------------------------------------------------------------ +// Enumeration histograms. +// +// For usage details, see the equivalents in histogram_macros.h. + +#define LOCAL_HISTOGRAM_ENUMERATION(name, ...) \ + CR_EXPAND_ARG(INTERNAL_UMA_HISTOGRAM_ENUMERATION_GET_MACRO( \ + __VA_ARGS__, INTERNAL_UMA_HISTOGRAM_ENUMERATION_SPECIFY_BOUNDARY, \ + INTERNAL_UMA_HISTOGRAM_ENUMERATION_DEDUCE_BOUNDARY)( \ + name, __VA_ARGS__, base::HistogramBase::kNoFlags)) + +#define LOCAL_HISTOGRAM_BOOLEAN(name, sample) \ + STATIC_HISTOGRAM_POINTER_BLOCK(name, AddBoolean(sample), \ + base::BooleanHistogram::FactoryGet(name, base::Histogram::kNoFlags)) + +//------------------------------------------------------------------------------ +// Percentage histograms. +// +// For usage details, see the equivalents in histogram_macros.h + +#define LOCAL_HISTOGRAM_PERCENTAGE(name, under_one_hundred) \ + LOCAL_HISTOGRAM_ENUMERATION(name, under_one_hundred, 101) + +//------------------------------------------------------------------------------ +// Count histograms. These are used for collecting numeric data. Note that we +// have macros for more specialized use cases below (memory, time, percentages). +// For usage details, see the equivalents in histogram_macros.h. + +#define LOCAL_HISTOGRAM_COUNTS_100(name, sample) \ + LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 100, 50) + +#define LOCAL_HISTOGRAM_COUNTS_10000(name, sample) \ + LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 10000, 50) + +#define LOCAL_HISTOGRAM_COUNTS_1000000(name, sample) \ + LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 1000000, 50) + +#define LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \ + INTERNAL_HISTOGRAM_CUSTOM_COUNTS_WITH_FLAG( \ + name, sample, min, max, bucket_count, base::HistogramBase::kNoFlags) + +//------------------------------------------------------------------------------ +// Timing histograms. These are used for collecting timing data (generally +// latencies). +// +// For usage details, see the equivalents in histogram_macros.h. + +#define LOCAL_HISTOGRAM_TIMES(name, sample) LOCAL_HISTOGRAM_CUSTOM_TIMES( \ + name, sample, base::TimeDelta::FromMilliseconds(1), \ + base::TimeDelta::FromSeconds(10), 50) + +#define LOCAL_HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \ + STATIC_HISTOGRAM_POINTER_BLOCK( \ + name, AddTimeMillisecondsGranularity(sample), \ + base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \ + base::HistogramBase::kNoFlags)) + +//------------------------------------------------------------------------------ +// Memory histograms. +// +// For usage details, see the equivalents in histogram_macros.h. + +#define LOCAL_HISTOGRAM_MEMORY_KB(name, sample) LOCAL_HISTOGRAM_CUSTOM_COUNTS( \ + name, sample, 1000, 500000, 50) + +//------------------------------------------------------------------------------ +// Deprecated histograms. Not recommended for current use. + +// TODO(rkaplow): See if we can clean up this macro and usage. +// Legacy non-explicit version. We suggest using LOCAL_HISTOGRAM_COUNTS_1000000 +// instead. +#define LOCAL_HISTOGRAM_COUNTS(name, sample) \ + LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 1000000, 50) + +#endif // BASE_METRICS_HISTOGRAM_MACROS_LOCAL_H_ diff --git a/native/zucchini/vendor/libchrome/base/metrics/histogram_samples.h b/native/zucchini/vendor/libchrome/base/metrics/histogram_samples.h new file mode 100644 index 0000000..6908873 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/metrics/histogram_samples.h @@ -0,0 +1,267 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_METRICS_HISTOGRAM_SAMPLES_H_ +#define BASE_METRICS_HISTOGRAM_SAMPLES_H_ + +#include +#include + +#include +#include + +#include "base/atomicops.h" +#include "base/macros.h" +#include "base/metrics/histogram_base.h" + +namespace base { + +class Pickle; +class PickleIterator; +class SampleCountIterator; + +// HistogramSamples is a container storing all samples of a histogram. All +// elements must be of a fixed width to ensure 32/64-bit interoperability. +// If this structure changes, bump the version number for kTypeIdHistogram +// in persistent_histogram_allocator.cc. +// +// Note that though these samples are individually consistent (through the use +// of atomic operations on the counts), there is only "eventual consistency" +// overall when multiple threads are accessing this data. That means that the +// sum, redundant-count, etc. could be momentarily out-of-sync with the stored +// counts but will settle to a consistent "steady state" once all threads have +// exited this code. +class BASE_EXPORT HistogramSamples { + public: + // A single bucket and count. To fit within a single atomic on 32-bit build + // architectures, both |bucket| and |count| are limited in size to 16 bits. + // This limits the functionality somewhat but if an entry can't fit then + // the full array of samples can be allocated and used. + struct SingleSample { + uint16_t bucket; + uint16_t count; + }; + + // A structure for managing an atomic single sample. Because this is generally + // used in association with other atomic values, the defined methods use + // acquire/release operations to guarantee ordering with outside values. + union BASE_EXPORT AtomicSingleSample { + AtomicSingleSample() : as_atomic(0) {} + AtomicSingleSample(subtle::Atomic32 rhs) : as_atomic(rhs) {} + + // Returns the single sample in an atomic manner. This in an "acquire" + // load. The returned sample isn't shared and thus its fields can be safely + // accessed. + SingleSample Load() const; + + // Extracts the single sample in an atomic manner. If |disable| is true + // then this object will be set so it will never accumulate another value. + // This is "no barrier" so doesn't enforce ordering with other atomic ops. + SingleSample Extract(bool disable); + + // Adds a given count to the held bucket. If not possible, it returns false + // and leaves the parts unchanged. Once extracted/disabled, this always + // returns false. This in an "acquire/release" operation. + bool Accumulate(size_t bucket, HistogramBase::Count count); + + // Returns if the sample has been "disabled" (via Extract) and thus not + // allowed to accept further accumulation. + bool IsDisabled() const; + + private: + // union field: The actual sample bucket and count. + SingleSample as_parts; + + // union field: The sample as an atomic value. Atomic64 would provide + // more flexibility but isn't available on all builds. This can hold a + // special, internal "disabled" value indicating that it must not accept + // further accumulation. + subtle::Atomic32 as_atomic; + }; + + // A structure of information about the data, common to all sample containers. + // Because of how this is used in persistent memory, it must be a POD object + // that makes sense when initialized to all zeros. + struct Metadata { + // Expected size for 32/64-bit check. + static constexpr size_t kExpectedInstanceSize = 24; + + // Initialized when the sample-set is first created with a value provided + // by the caller. It is generally used to identify the sample-set across + // threads and processes, though not necessarily uniquely as it is possible + // to have multiple sample-sets representing subsets of the data. + uint64_t id; + + // The sum of all the entries, effectivly the sum(sample * count) for + // all samples. Despite being atomic, no guarantees are made on the + // accuracy of this value; there may be races during histogram + // accumulation and snapshotting that we choose to accept. It should + // be treated as approximate. +#ifdef ARCH_CPU_64_BITS + subtle::Atomic64 sum; +#else + // 32-bit systems don't have atomic 64-bit operations. Use a basic type + // and don't worry about "shearing". + int64_t sum; +#endif + + // A "redundant" count helps identify memory corruption. It redundantly + // stores the total number of samples accumulated in the histogram. We + // can compare this count to the sum of the counts (TotalCount() function), + // and detect problems. Note, depending on the implementation of different + // histogram types, there might be races during histogram accumulation + // and snapshotting that we choose to accept. In this case, the tallies + // might mismatch even when no memory corruption has happened. + HistogramBase::AtomicCount redundant_count; + + // A single histogram value and associated count. This allows histograms + // that typically report only a single value to not require full storage + // to be allocated. + AtomicSingleSample single_sample; // 32 bits + }; + + // Because structures held in persistent memory must be POD, there can be no + // default constructor to clear the fields. This derived class exists just + // to clear them when being allocated on the heap. + struct BASE_EXPORT LocalMetadata : Metadata { + LocalMetadata(); + }; + + HistogramSamples(uint64_t id, Metadata* meta); + virtual ~HistogramSamples(); + + virtual void Accumulate(HistogramBase::Sample value, + HistogramBase::Count count) = 0; + virtual HistogramBase::Count GetCount(HistogramBase::Sample value) const = 0; + virtual HistogramBase::Count TotalCount() const = 0; + + virtual void Add(const HistogramSamples& other); + + // Add from serialized samples. + virtual bool AddFromPickle(PickleIterator* iter); + + virtual void Subtract(const HistogramSamples& other); + + virtual std::unique_ptr Iterator() const = 0; + virtual void Serialize(Pickle* pickle) const; + + // Accessor fuctions. + uint64_t id() const { return meta_->id; } + int64_t sum() const { +#ifdef ARCH_CPU_64_BITS + return subtle::NoBarrier_Load(&meta_->sum); +#else + return meta_->sum; +#endif + } + HistogramBase::Count redundant_count() const { + return subtle::NoBarrier_Load(&meta_->redundant_count); + } + + protected: + enum NegativeSampleReason { + SAMPLES_HAVE_LOGGED_BUT_NOT_SAMPLE, + SAMPLES_SAMPLE_LESS_THAN_LOGGED, + SAMPLES_ADDED_NEGATIVE_COUNT, + SAMPLES_ADD_WENT_NEGATIVE, + SAMPLES_ADD_OVERFLOW, + SAMPLES_ACCUMULATE_NEGATIVE_COUNT, + SAMPLES_ACCUMULATE_WENT_NEGATIVE, + DEPRECATED_SAMPLES_ACCUMULATE_OVERFLOW, + SAMPLES_ACCUMULATE_OVERFLOW, + MAX_NEGATIVE_SAMPLE_REASONS + }; + + // Based on |op| type, add or subtract sample counts data from the iterator. + enum Operator { ADD, SUBTRACT }; + virtual bool AddSubtractImpl(SampleCountIterator* iter, Operator op) = 0; + + // Accumulates to the embedded single-sample field if possible. Returns true + // on success, false otherwise. Sum and redundant-count are also updated in + // the success case. + bool AccumulateSingleSample(HistogramBase::Sample value, + HistogramBase::Count count, + size_t bucket); + + // Atomically adjust the sum and redundant-count. + void IncreaseSumAndCount(int64_t sum, HistogramBase::Count count); + + // Record a negative-sample observation and the reason why. + void RecordNegativeSample(NegativeSampleReason reason, + HistogramBase::Count increment); + + AtomicSingleSample& single_sample() { return meta_->single_sample; } + const AtomicSingleSample& single_sample() const { + return meta_->single_sample; + } + + Metadata* meta() { return meta_; } + + private: + // Depending on derived class meta values can come from local stoarge or + // external storage in which case HistogramSamples class cannot take ownership + // of Metadata*. + Metadata* meta_; + + DISALLOW_COPY_AND_ASSIGN(HistogramSamples); +}; + +class BASE_EXPORT SampleCountIterator { + public: + virtual ~SampleCountIterator(); + + virtual bool Done() const = 0; + virtual void Next() = 0; + + // Get the sample and count at current position. + // |min| |max| and |count| can be NULL if the value is not of interest. + // Note: |max| is int64_t because histograms support logged values in the + // full int32_t range and bucket max is exclusive, so it needs to support + // values up to MAXINT32+1. + // Requires: !Done(); + virtual void Get(HistogramBase::Sample* min, + int64_t* max, + HistogramBase::Count* count) const = 0; + static_assert(std::numeric_limits::max() < + std::numeric_limits::max(), + "Get() |max| must be able to hold Histogram::Sample max + 1"); + + // Get the index of current histogram bucket. + // For histograms that don't use predefined buckets, it returns false. + // Requires: !Done(); + virtual bool GetBucketIndex(size_t* index) const; +}; + +class BASE_EXPORT SingleSampleIterator : public SampleCountIterator { + public: + SingleSampleIterator(HistogramBase::Sample min, + int64_t max, + HistogramBase::Count count); + SingleSampleIterator(HistogramBase::Sample min, + int64_t max, + HistogramBase::Count count, + size_t bucket_index); + ~SingleSampleIterator() override; + + // SampleCountIterator: + bool Done() const override; + void Next() override; + void Get(HistogramBase::Sample* min, + int64_t* max, + HistogramBase::Count* count) const override; + + // SampleVector uses predefined buckets so iterator can return bucket index. + bool GetBucketIndex(size_t* index) const override; + + private: + // Information about the single value to return. + const HistogramBase::Sample min_; + const int64_t max_; + const size_t bucket_index_; + HistogramBase::Count count_; +}; + +} // namespace base + +#endif // BASE_METRICS_HISTOGRAM_SAMPLES_H_ diff --git a/native/zucchini/vendor/libchrome/base/metrics/persistent_memory_allocator.cc b/native/zucchini/vendor/libchrome/base/metrics/persistent_memory_allocator.cc new file mode 100644 index 0000000..9b18a00 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/metrics/persistent_memory_allocator.cc @@ -0,0 +1,1204 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/metrics/persistent_memory_allocator.h" + +#include +#include + +#if defined(OS_WIN) +#include +#include "winbase.h" +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) +#include +#endif + +#include "base/files/memory_mapped_file.h" +#include "base/logging.h" +#include "base/memory/shared_memory.h" +#include "base/metrics/histogram_functions.h" +#include "base/metrics/sparse_histogram.h" +#include "base/numerics/safe_conversions.h" +#include "base/sys_info.h" +#include "base/threading/thread_restrictions.h" +#include "build/build_config.h" + +namespace { + +// Limit of memory segment size. It has to fit in an unsigned 32-bit number +// and should be a power of 2 in order to accomodate almost any page size. +const uint32_t kSegmentMaxSize = 1 << 30; // 1 GiB + +// A constant (random) value placed in the shared metadata to identify +// an already initialized memory segment. +const uint32_t kGlobalCookie = 0x408305DC; + +// The current version of the metadata. If updates are made that change +// the metadata, the version number can be queried to operate in a backward- +// compatible manner until the memory segment is completely re-initalized. +const uint32_t kGlobalVersion = 2; + +// Constant values placed in the block headers to indicate its state. +const uint32_t kBlockCookieFree = 0; +const uint32_t kBlockCookieQueue = 1; +const uint32_t kBlockCookieWasted = (uint32_t)-1; +const uint32_t kBlockCookieAllocated = 0xC8799269; + +// TODO(bcwhite): When acceptable, consider moving flags to std::atomic +// types rather than combined bitfield. + +// Flags stored in the flags_ field of the SharedMetadata structure below. +enum : int { + kFlagCorrupt = 1 << 0, + kFlagFull = 1 << 1 +}; + +// Errors that are logged in "errors" histogram. +enum AllocatorError : int { + kMemoryIsCorrupt = 1, +}; + +bool CheckFlag(const volatile std::atomic* flags, int flag) { + uint32_t loaded_flags = flags->load(std::memory_order_relaxed); + return (loaded_flags & flag) != 0; +} + +void SetFlag(volatile std::atomic* flags, int flag) { + uint32_t loaded_flags = flags->load(std::memory_order_relaxed); + for (;;) { + uint32_t new_flags = (loaded_flags & ~flag) | flag; + // In the failue case, actual "flags" value stored in loaded_flags. + // These access are "relaxed" because they are completely independent + // of all other values. + if (flags->compare_exchange_weak(loaded_flags, new_flags, + std::memory_order_relaxed, + std::memory_order_relaxed)) { + break; + } + } +} + +} // namespace + +namespace base { + +// All allocations and data-structures must be aligned to this byte boundary. +// Alignment as large as the physical bus between CPU and RAM is _required_ +// for some architectures, is simply more efficient on other CPUs, and +// generally a Good Idea(tm) for all platforms as it reduces/eliminates the +// chance that a type will span cache lines. Alignment mustn't be less +// than 8 to ensure proper alignment for all types. The rest is a balance +// between reducing spans across multiple cache lines and wasted space spent +// padding out allocations. An alignment of 16 would ensure that the block +// header structure always sits in a single cache line. An average of about +// 1/2 this value will be wasted with every allocation. +const uint32_t PersistentMemoryAllocator::kAllocAlignment = 8; + +// The block-header is placed at the top of every allocation within the +// segment to describe the data that follows it. +struct PersistentMemoryAllocator::BlockHeader { + uint32_t size; // Number of bytes in this block, including header. + uint32_t cookie; // Constant value indicating completed allocation. + std::atomic type_id; // Arbitrary number indicating data type. + std::atomic next; // Pointer to the next block when iterating. +}; + +// The shared metadata exists once at the top of the memory segment to +// describe the state of the allocator to all processes. The size of this +// structure must be a multiple of 64-bits to ensure compatibility between +// architectures. +struct PersistentMemoryAllocator::SharedMetadata { + uint32_t cookie; // Some value that indicates complete initialization. + uint32_t size; // Total size of memory segment. + uint32_t page_size; // Paging size within memory segment. + uint32_t version; // Version code so upgrades don't break. + uint64_t id; // Arbitrary ID number given by creator. + uint32_t name; // Reference to stored name string. + uint32_t padding1; // Pad-out read-only data to 64-bit alignment. + + // Above is read-only after first construction. Below may be changed and + // so must be marked "volatile" to provide correct inter-process behavior. + + // State of the memory, plus some padding to keep alignment. + volatile std::atomic memory_state; // MemoryState enum values. + uint8_t padding2[3]; + + // Bitfield of information flags. Access to this should be done through + // the CheckFlag() and SetFlag() methods defined above. + volatile std::atomic flags; + + // Offset/reference to first free space in segment. + volatile std::atomic freeptr; + + // The "iterable" queue is an M&S Queue as described here, append-only: + // https://www.research.ibm.com/people/m/michael/podc-1996.pdf + // |queue| needs to be 64-bit aligned and is itself a multiple of 64 bits. + volatile std::atomic tailptr; // Last block of iteration queue. + volatile BlockHeader queue; // Empty block for linked-list head/tail. +}; + +// The "queue" block header is used to detect "last node" so that zero/null +// can be used to indicate that it hasn't been added at all. It is part of +// the SharedMetadata structure which itself is always located at offset zero. +const PersistentMemoryAllocator::Reference + PersistentMemoryAllocator::kReferenceQueue = + offsetof(SharedMetadata, queue); + +const base::FilePath::CharType PersistentMemoryAllocator::kFileExtension[] = + FILE_PATH_LITERAL(".pma"); + + +PersistentMemoryAllocator::Iterator::Iterator( + const PersistentMemoryAllocator* allocator) + : allocator_(allocator), last_record_(kReferenceQueue), record_count_(0) {} + +PersistentMemoryAllocator::Iterator::Iterator( + const PersistentMemoryAllocator* allocator, + Reference starting_after) + : allocator_(allocator), last_record_(0), record_count_(0) { + Reset(starting_after); +} + +void PersistentMemoryAllocator::Iterator::Reset() { + last_record_.store(kReferenceQueue, std::memory_order_relaxed); + record_count_.store(0, std::memory_order_relaxed); +} + +void PersistentMemoryAllocator::Iterator::Reset(Reference starting_after) { + if (starting_after == 0) { + Reset(); + return; + } + + last_record_.store(starting_after, std::memory_order_relaxed); + record_count_.store(0, std::memory_order_relaxed); + + // Ensure that the starting point is a valid, iterable block (meaning it can + // be read and has a non-zero "next" pointer). + const volatile BlockHeader* block = + allocator_->GetBlock(starting_after, 0, 0, false, false); + if (!block || block->next.load(std::memory_order_relaxed) == 0) { + NOTREACHED(); + last_record_.store(kReferenceQueue, std::memory_order_release); + } +} + +PersistentMemoryAllocator::Reference +PersistentMemoryAllocator::Iterator::GetLast() { + Reference last = last_record_.load(std::memory_order_relaxed); + if (last == kReferenceQueue) + return kReferenceNull; + return last; +} + +PersistentMemoryAllocator::Reference +PersistentMemoryAllocator::Iterator::GetNext(uint32_t* type_return) { + // Make a copy of the existing count of found-records, acquiring all changes + // made to the allocator, notably "freeptr" (see comment in loop for why + // the load of that value cannot be moved above here) that occurred during + // any previous runs of this method, including those by parallel threads + // that interrupted it. It pairs with the Release at the end of this method. + // + // Otherwise, if the compiler were to arrange the two loads such that + // "count" was fetched _after_ "freeptr" then it would be possible for + // this thread to be interrupted between them and other threads perform + // multiple allocations, make-iterables, and iterations (with the included + // increment of |record_count_|) culminating in the check at the bottom + // mistakenly determining that a loop exists. Isn't this stuff fun? + uint32_t count = record_count_.load(std::memory_order_acquire); + + Reference last = last_record_.load(std::memory_order_acquire); + Reference next; + while (true) { + const volatile BlockHeader* block = + allocator_->GetBlock(last, 0, 0, true, false); + if (!block) // Invalid iterator state. + return kReferenceNull; + + // The compiler and CPU can freely reorder all memory accesses on which + // there are no dependencies. It could, for example, move the load of + // "freeptr" to above this point because there are no explicit dependencies + // between it and "next". If it did, however, then another block could + // be queued after that but before the following load meaning there is + // one more queued block than the future "detect loop by having more + // blocks that could fit before freeptr" will allow. + // + // By "acquiring" the "next" value here, it's synchronized to the enqueue + // of the node which in turn is synchronized to the allocation (which sets + // freeptr). Thus, the scenario above cannot happen. + next = block->next.load(std::memory_order_acquire); + if (next == kReferenceQueue) // No next allocation in queue. + return kReferenceNull; + block = allocator_->GetBlock(next, 0, 0, false, false); + if (!block) { // Memory is corrupt. + allocator_->SetCorrupt(); + return kReferenceNull; + } + + // Update the "last_record" pointer to be the reference being returned. + // If it fails then another thread has already iterated past it so loop + // again. Failing will also load the existing value into "last" so there + // is no need to do another such load when the while-loop restarts. A + // "strong" compare-exchange is used because failing unnecessarily would + // mean repeating some fairly costly validations above. + if (last_record_.compare_exchange_strong( + last, next, std::memory_order_acq_rel, std::memory_order_acquire)) { + *type_return = block->type_id.load(std::memory_order_relaxed); + break; + } + } + + // Memory corruption could cause a loop in the list. Such must be detected + // so as to not cause an infinite loop in the caller. This is done by simply + // making sure it doesn't iterate more times than the absolute maximum + // number of allocations that could have been made. Callers are likely + // to loop multiple times before it is detected but at least it stops. + const uint32_t freeptr = std::min( + allocator_->shared_meta()->freeptr.load(std::memory_order_relaxed), + allocator_->mem_size_); + const uint32_t max_records = + freeptr / (sizeof(BlockHeader) + kAllocAlignment); + if (count > max_records) { + allocator_->SetCorrupt(); + return kReferenceNull; + } + + // Increment the count and release the changes made above. It pairs with + // the Acquire at the top of this method. Note that this operation is not + // strictly synchonized with fetching of the object to return, which would + // have to be done inside the loop and is somewhat complicated to achieve. + // It does not matter if it falls behind temporarily so long as it never + // gets ahead. + record_count_.fetch_add(1, std::memory_order_release); + return next; +} + +PersistentMemoryAllocator::Reference +PersistentMemoryAllocator::Iterator::GetNextOfType(uint32_t type_match) { + Reference ref; + uint32_t type_found; + while ((ref = GetNext(&type_found)) != 0) { + if (type_found == type_match) + return ref; + } + return kReferenceNull; +} + + +// static +bool PersistentMemoryAllocator::IsMemoryAcceptable(const void* base, + size_t size, + size_t page_size, + bool readonly) { + return ((base && reinterpret_cast(base) % kAllocAlignment == 0) && + (size >= sizeof(SharedMetadata) && size <= kSegmentMaxSize) && + (size % kAllocAlignment == 0 || readonly) && + (page_size == 0 || size % page_size == 0 || readonly)); +} + +PersistentMemoryAllocator::PersistentMemoryAllocator(void* base, + size_t size, + size_t page_size, + uint64_t id, + base::StringPiece name, + bool readonly) + : PersistentMemoryAllocator(Memory(base, MEM_EXTERNAL), + size, + page_size, + id, + name, + readonly) {} + +PersistentMemoryAllocator::PersistentMemoryAllocator(Memory memory, + size_t size, + size_t page_size, + uint64_t id, + base::StringPiece name, + bool readonly) + : mem_base_(static_cast(memory.base)), + mem_type_(memory.type), + mem_size_(static_cast(size)), + mem_page_(static_cast((page_size ? page_size : size))), +#if defined(OS_NACL) + vm_page_size_(4096U), // SysInfo is not built for NACL. +#else + vm_page_size_(SysInfo::VMAllocationGranularity()), +#endif + readonly_(readonly), + corrupt_(0), + allocs_histogram_(nullptr), + used_histogram_(nullptr), + errors_histogram_(nullptr) { + // These asserts ensure that the structures are 32/64-bit agnostic and meet + // all the requirements of use within the allocator. They access private + // definitions and so cannot be moved to the global scope. + static_assert(sizeof(PersistentMemoryAllocator::BlockHeader) == 16, + "struct is not portable across different natural word widths"); + static_assert(sizeof(PersistentMemoryAllocator::SharedMetadata) == 64, + "struct is not portable across different natural word widths"); + + static_assert(sizeof(BlockHeader) % kAllocAlignment == 0, + "BlockHeader is not a multiple of kAllocAlignment"); + static_assert(sizeof(SharedMetadata) % kAllocAlignment == 0, + "SharedMetadata is not a multiple of kAllocAlignment"); + static_assert(kReferenceQueue % kAllocAlignment == 0, + "\"queue\" is not aligned properly; must be at end of struct"); + + // Ensure that memory segment is of acceptable size. + CHECK(IsMemoryAcceptable(memory.base, size, page_size, readonly)); + + // These atomics operate inter-process and so must be lock-free. The local + // casts are to make sure it can be evaluated at compile time to a constant. + CHECK(((SharedMetadata*)nullptr)->freeptr.is_lock_free()); + CHECK(((SharedMetadata*)nullptr)->flags.is_lock_free()); + CHECK(((BlockHeader*)nullptr)->next.is_lock_free()); + CHECK(corrupt_.is_lock_free()); + + if (shared_meta()->cookie != kGlobalCookie) { + if (readonly) { + SetCorrupt(); + return; + } + + // This block is only executed when a completely new memory segment is + // being initialized. It's unshared and single-threaded... + volatile BlockHeader* const first_block = + reinterpret_cast(mem_base_ + + sizeof(SharedMetadata)); + if (shared_meta()->cookie != 0 || + shared_meta()->size != 0 || + shared_meta()->version != 0 || + shared_meta()->freeptr.load(std::memory_order_relaxed) != 0 || + shared_meta()->flags.load(std::memory_order_relaxed) != 0 || + shared_meta()->id != 0 || + shared_meta()->name != 0 || + shared_meta()->tailptr != 0 || + shared_meta()->queue.cookie != 0 || + shared_meta()->queue.next.load(std::memory_order_relaxed) != 0 || + first_block->size != 0 || + first_block->cookie != 0 || + first_block->type_id.load(std::memory_order_relaxed) != 0 || + first_block->next != 0) { + // ...or something malicious has been playing with the metadata. + SetCorrupt(); + } + + // This is still safe to do even if corruption has been detected. + shared_meta()->cookie = kGlobalCookie; + shared_meta()->size = mem_size_; + shared_meta()->page_size = mem_page_; + shared_meta()->version = kGlobalVersion; + shared_meta()->id = id; + shared_meta()->freeptr.store(sizeof(SharedMetadata), + std::memory_order_release); + + // Set up the queue of iterable allocations. + shared_meta()->queue.size = sizeof(BlockHeader); + shared_meta()->queue.cookie = kBlockCookieQueue; + shared_meta()->queue.next.store(kReferenceQueue, std::memory_order_release); + shared_meta()->tailptr.store(kReferenceQueue, std::memory_order_release); + + // Allocate space for the name so other processes can learn it. + if (!name.empty()) { + const size_t name_length = name.length() + 1; + shared_meta()->name = Allocate(name_length, 0); + char* name_cstr = GetAsArray(shared_meta()->name, 0, name_length); + if (name_cstr) + memcpy(name_cstr, name.data(), name.length()); + } + + shared_meta()->memory_state.store(MEMORY_INITIALIZED, + std::memory_order_release); + } else { + if (shared_meta()->size == 0 || shared_meta()->version != kGlobalVersion || + shared_meta()->freeptr.load(std::memory_order_relaxed) == 0 || + shared_meta()->tailptr == 0 || shared_meta()->queue.cookie == 0 || + shared_meta()->queue.next.load(std::memory_order_relaxed) == 0) { + SetCorrupt(); + } + if (!readonly) { + // The allocator is attaching to a previously initialized segment of + // memory. If the initialization parameters differ, make the best of it + // by reducing the local construction parameters to match those of + // the actual memory area. This ensures that the local object never + // tries to write outside of the original bounds. + // Because the fields are const to ensure that no code other than the + // constructor makes changes to them as well as to give optimization + // hints to the compiler, it's necessary to const-cast them for changes + // here. + if (shared_meta()->size < mem_size_) + *const_cast(&mem_size_) = shared_meta()->size; + if (shared_meta()->page_size < mem_page_) + *const_cast(&mem_page_) = shared_meta()->page_size; + + // Ensure that settings are still valid after the above adjustments. + if (!IsMemoryAcceptable(memory.base, mem_size_, mem_page_, readonly)) + SetCorrupt(); + } + } +} + +PersistentMemoryAllocator::~PersistentMemoryAllocator() { + // It's strictly forbidden to do any memory access here in case there is + // some issue with the underlying memory segment. The "Local" allocator + // makes use of this to allow deletion of the segment on the heap from + // within its destructor. +} + +uint64_t PersistentMemoryAllocator::Id() const { + return shared_meta()->id; +} + +const char* PersistentMemoryAllocator::Name() const { + Reference name_ref = shared_meta()->name; + const char* name_cstr = + GetAsArray(name_ref, 0, PersistentMemoryAllocator::kSizeAny); + if (!name_cstr) + return ""; + + size_t name_length = GetAllocSize(name_ref); + if (name_cstr[name_length - 1] != '\0') { + NOTREACHED(); + SetCorrupt(); + return ""; + } + + return name_cstr; +} + +void PersistentMemoryAllocator::CreateTrackingHistograms( + base::StringPiece name) { + if (name.empty() || readonly_) + return; + std::string name_string = name.as_string(); + +#if 0 + // This histogram wasn't being used so has been disabled. It is left here + // in case development of a new use of the allocator could benefit from + // recording (temporarily and locally) the allocation sizes. + DCHECK(!allocs_histogram_); + allocs_histogram_ = Histogram::FactoryGet( + "UMA.PersistentAllocator." + name_string + ".Allocs", 1, 10000, 50, + HistogramBase::kUmaTargetedHistogramFlag); +#endif + + DCHECK(!used_histogram_); + used_histogram_ = LinearHistogram::FactoryGet( + "UMA.PersistentAllocator." + name_string + ".UsedPct", 1, 101, 21, + HistogramBase::kUmaTargetedHistogramFlag); + + DCHECK(!errors_histogram_); + errors_histogram_ = SparseHistogram::FactoryGet( + "UMA.PersistentAllocator." + name_string + ".Errors", + HistogramBase::kUmaTargetedHistogramFlag); +} + +void PersistentMemoryAllocator::Flush(bool sync) { + FlushPartial(used(), sync); +} + +void PersistentMemoryAllocator::SetMemoryState(uint8_t memory_state) { + shared_meta()->memory_state.store(memory_state, std::memory_order_relaxed); + FlushPartial(sizeof(SharedMetadata), false); +} + +uint8_t PersistentMemoryAllocator::GetMemoryState() const { + return shared_meta()->memory_state.load(std::memory_order_relaxed); +} + +size_t PersistentMemoryAllocator::used() const { + return std::min(shared_meta()->freeptr.load(std::memory_order_relaxed), + mem_size_); +} + +PersistentMemoryAllocator::Reference PersistentMemoryAllocator::GetAsReference( + const void* memory, + uint32_t type_id) const { + uintptr_t address = reinterpret_cast(memory); + if (address < reinterpret_cast(mem_base_)) + return kReferenceNull; + + uintptr_t offset = address - reinterpret_cast(mem_base_); + if (offset >= mem_size_ || offset < sizeof(BlockHeader)) + return kReferenceNull; + + Reference ref = static_cast(offset) - sizeof(BlockHeader); + if (!GetBlockData(ref, type_id, kSizeAny)) + return kReferenceNull; + + return ref; +} + +size_t PersistentMemoryAllocator::GetAllocSize(Reference ref) const { + const volatile BlockHeader* const block = GetBlock(ref, 0, 0, false, false); + if (!block) + return 0; + uint32_t size = block->size; + // Header was verified by GetBlock() but a malicious actor could change + // the value between there and here. Check it again. + if (size <= sizeof(BlockHeader) || ref + size > mem_size_) { + SetCorrupt(); + return 0; + } + return size - sizeof(BlockHeader); +} + +uint32_t PersistentMemoryAllocator::GetType(Reference ref) const { + const volatile BlockHeader* const block = GetBlock(ref, 0, 0, false, false); + if (!block) + return 0; + return block->type_id.load(std::memory_order_relaxed); +} + +bool PersistentMemoryAllocator::ChangeType(Reference ref, + uint32_t to_type_id, + uint32_t from_type_id, + bool clear) { + DCHECK(!readonly_); + volatile BlockHeader* const block = GetBlock(ref, 0, 0, false, false); + if (!block) + return false; + + // "Strong" exchanges are used below because there is no loop that can retry + // in the wake of spurious failures possible with "weak" exchanges. It is, + // in aggregate, an "acquire-release" operation so no memory accesses can be + // reordered either before or after this method (since changes based on type + // could happen on either side). + + if (clear) { + // If clearing the memory, first change it to the "transitioning" type so + // there can be no confusion by other threads. After the memory is cleared, + // it can be changed to its final type. + if (!block->type_id.compare_exchange_strong( + from_type_id, kTypeIdTransitioning, std::memory_order_acquire, + std::memory_order_acquire)) { + // Existing type wasn't what was expected: fail (with no changes) + return false; + } + + // Clear the memory in an atomic manner. Using "release" stores force + // every write to be done after the ones before it. This is better than + // using memset because (a) it supports "volatile" and (b) it creates a + // reliable pattern upon which other threads may rely. + volatile std::atomic* data = + reinterpret_cast*>( + reinterpret_cast(block) + sizeof(BlockHeader)); + const uint32_t words = (block->size - sizeof(BlockHeader)) / sizeof(int); + DCHECK_EQ(0U, (block->size - sizeof(BlockHeader)) % sizeof(int)); + for (uint32_t i = 0; i < words; ++i) { + data->store(0, std::memory_order_release); + ++data; + } + + // If the destination type is "transitioning" then skip the final exchange. + if (to_type_id == kTypeIdTransitioning) + return true; + + // Finish the change to the desired type. + from_type_id = kTypeIdTransitioning; // Exchange needs modifiable original. + bool success = block->type_id.compare_exchange_strong( + from_type_id, to_type_id, std::memory_order_release, + std::memory_order_relaxed); + DCHECK(success); // Should never fail. + return success; + } + + // One step change to the new type. Will return false if the existing value + // doesn't match what is expected. + return block->type_id.compare_exchange_strong(from_type_id, to_type_id, + std::memory_order_acq_rel, + std::memory_order_acquire); +} + +PersistentMemoryAllocator::Reference PersistentMemoryAllocator::Allocate( + size_t req_size, + uint32_t type_id) { + Reference ref = AllocateImpl(req_size, type_id); + if (ref) { + // Success: Record this allocation in usage stats (if active). + if (allocs_histogram_) + allocs_histogram_->Add(static_cast(req_size)); + } else { + // Failure: Record an allocation of zero for tracking. + if (allocs_histogram_) + allocs_histogram_->Add(0); + } + return ref; +} + +PersistentMemoryAllocator::Reference PersistentMemoryAllocator::AllocateImpl( + size_t req_size, + uint32_t type_id) { + DCHECK(!readonly_); + + // Validate req_size to ensure it won't overflow when used as 32-bit value. + if (req_size > kSegmentMaxSize - sizeof(BlockHeader)) { + NOTREACHED(); + return kReferenceNull; + } + + // Round up the requested size, plus header, to the next allocation alignment. + uint32_t size = static_cast(req_size + sizeof(BlockHeader)); + size = (size + (kAllocAlignment - 1)) & ~(kAllocAlignment - 1); + if (size <= sizeof(BlockHeader) || size > mem_page_) { + NOTREACHED(); + return kReferenceNull; + } + + // Get the current start of unallocated memory. Other threads may + // update this at any time and cause us to retry these operations. + // This value should be treated as "const" to avoid confusion through + // the code below but recognize that any failed compare-exchange operation + // involving it will cause it to be loaded with a more recent value. The + // code should either exit or restart the loop in that case. + /* const */ uint32_t freeptr = + shared_meta()->freeptr.load(std::memory_order_acquire); + + // Allocation is lockless so we do all our caculation and then, if saving + // indicates a change has occurred since we started, scrap everything and + // start over. + for (;;) { + if (IsCorrupt()) + return kReferenceNull; + + if (freeptr + size > mem_size_) { + SetFlag(&shared_meta()->flags, kFlagFull); + return kReferenceNull; + } + + // Get pointer to the "free" block. If something has been allocated since + // the load of freeptr above, it is still safe as nothing will be written + // to that location until after the compare-exchange below. + volatile BlockHeader* const block = GetBlock(freeptr, 0, 0, false, true); + if (!block) { + SetCorrupt(); + return kReferenceNull; + } + + // An allocation cannot cross page boundaries. If it would, create a + // "wasted" block and begin again at the top of the next page. This + // area could just be left empty but we fill in the block header just + // for completeness sake. + const uint32_t page_free = mem_page_ - freeptr % mem_page_; + if (size > page_free) { + if (page_free <= sizeof(BlockHeader)) { + SetCorrupt(); + return kReferenceNull; + } + const uint32_t new_freeptr = freeptr + page_free; + if (shared_meta()->freeptr.compare_exchange_strong( + freeptr, new_freeptr, std::memory_order_acq_rel, + std::memory_order_acquire)) { + block->size = page_free; + block->cookie = kBlockCookieWasted; + } + continue; + } + + // Don't leave a slice at the end of a page too small for anything. This + // can result in an allocation up to two alignment-sizes greater than the + // minimum required by requested-size + header + alignment. + if (page_free - size < sizeof(BlockHeader) + kAllocAlignment) + size = page_free; + + const uint32_t new_freeptr = freeptr + size; + if (new_freeptr > mem_size_) { + SetCorrupt(); + return kReferenceNull; + } + + // Save our work. Try again if another thread has completed an allocation + // while we were processing. A "weak" exchange would be permissable here + // because the code will just loop and try again but the above processing + // is significant so make the extra effort of a "strong" exchange. + if (!shared_meta()->freeptr.compare_exchange_strong( + freeptr, new_freeptr, std::memory_order_acq_rel, + std::memory_order_acquire)) { + continue; + } + + // Given that all memory was zeroed before ever being given to an instance + // of this class and given that we only allocate in a monotomic fashion + // going forward, it must be that the newly allocated block is completely + // full of zeros. If we find anything in the block header that is NOT a + // zero then something must have previously run amuck through memory, + // writing beyond the allocated space and into unallocated space. + if (block->size != 0 || + block->cookie != kBlockCookieFree || + block->type_id.load(std::memory_order_relaxed) != 0 || + block->next.load(std::memory_order_relaxed) != 0) { + SetCorrupt(); + return kReferenceNull; + } + + // Make sure the memory exists by writing to the first byte of every memory + // page it touches beyond the one containing the block header itself. + // As the underlying storage is often memory mapped from disk or shared + // space, sometimes things go wrong and those address don't actually exist + // leading to a SIGBUS (or Windows equivalent) at some arbitrary location + // in the code. This should concentrate all those failures into this + // location for easy tracking and, eventually, proper handling. + volatile char* mem_end = reinterpret_cast(block) + size; + volatile char* mem_begin = reinterpret_cast( + (reinterpret_cast(block) + sizeof(BlockHeader) + + (vm_page_size_ - 1)) & + ~static_cast(vm_page_size_ - 1)); + for (volatile char* memory = mem_begin; memory < mem_end; + memory += vm_page_size_) { + // It's required that a memory segment start as all zeros and thus the + // newly allocated block is all zeros at this point. Thus, writing a + // zero to it allows testing that the memory exists without actually + // changing its contents. The compiler doesn't know about the requirement + // and so cannot optimize-away these writes. + *memory = 0; + } + + // Load information into the block header. There is no "release" of the + // data here because this memory can, currently, be seen only by the thread + // performing the allocation. When it comes time to share this, the thread + // will call MakeIterable() which does the release operation. + block->size = size; + block->cookie = kBlockCookieAllocated; + block->type_id.store(type_id, std::memory_order_relaxed); + return freeptr; + } +} + +void PersistentMemoryAllocator::GetMemoryInfo(MemoryInfo* meminfo) const { + uint32_t remaining = std::max( + mem_size_ - shared_meta()->freeptr.load(std::memory_order_relaxed), + (uint32_t)sizeof(BlockHeader)); + meminfo->total = mem_size_; + meminfo->free = remaining - sizeof(BlockHeader); +} + +void PersistentMemoryAllocator::MakeIterable(Reference ref) { + DCHECK(!readonly_); + if (IsCorrupt()) + return; + volatile BlockHeader* block = GetBlock(ref, 0, 0, false, false); + if (!block) // invalid reference + return; + if (block->next.load(std::memory_order_acquire) != 0) // Already iterable. + return; + block->next.store(kReferenceQueue, std::memory_order_release); // New tail. + + // Try to add this block to the tail of the queue. May take multiple tries. + // If so, tail will be automatically updated with a more recent value during + // compare-exchange operations. + uint32_t tail = shared_meta()->tailptr.load(std::memory_order_acquire); + for (;;) { + // Acquire the current tail-pointer released by previous call to this + // method and validate it. + block = GetBlock(tail, 0, 0, true, false); + if (!block) { + SetCorrupt(); + return; + } + + // Try to insert the block at the tail of the queue. The tail node always + // has an existing value of kReferenceQueue; if that is somehow not the + // existing value then another thread has acted in the meantime. A "strong" + // exchange is necessary so the "else" block does not get executed when + // that is not actually the case (which can happen with a "weak" exchange). + uint32_t next = kReferenceQueue; // Will get replaced with existing value. + if (block->next.compare_exchange_strong(next, ref, + std::memory_order_acq_rel, + std::memory_order_acquire)) { + // Update the tail pointer to the new offset. If the "else" clause did + // not exist, then this could be a simple Release_Store to set the new + // value but because it does, it's possible that other threads could add + // one or more nodes at the tail before reaching this point. We don't + // have to check the return value because it either operates correctly + // or the exact same operation has already been done (by the "else" + // clause) on some other thread. + shared_meta()->tailptr.compare_exchange_strong(tail, ref, + std::memory_order_release, + std::memory_order_relaxed); + return; + } else { + // In the unlikely case that a thread crashed or was killed between the + // update of "next" and the update of "tailptr", it is necessary to + // perform the operation that would have been done. There's no explicit + // check for crash/kill which means that this operation may also happen + // even when the other thread is in perfect working order which is what + // necessitates the CompareAndSwap above. + shared_meta()->tailptr.compare_exchange_strong(tail, next, + std::memory_order_acq_rel, + std::memory_order_acquire); + } + } +} + +// The "corrupted" state is held both locally and globally (shared). The +// shared flag can't be trusted since a malicious actor could overwrite it. +// Because corruption can be detected during read-only operations such as +// iteration, this method may be called by other "const" methods. In this +// case, it's safe to discard the constness and modify the local flag and +// maybe even the shared flag if the underlying data isn't actually read-only. +void PersistentMemoryAllocator::SetCorrupt() const { + if (!corrupt_.load(std::memory_order_relaxed) && + !CheckFlag( + const_cast*>(&shared_meta()->flags), + kFlagCorrupt)) { + LOG(ERROR) << "Corruption detected in shared-memory segment."; + RecordError(kMemoryIsCorrupt); + } + + corrupt_.store(true, std::memory_order_relaxed); + if (!readonly_) { + SetFlag(const_cast*>(&shared_meta()->flags), + kFlagCorrupt); + } +} + +bool PersistentMemoryAllocator::IsCorrupt() const { + if (corrupt_.load(std::memory_order_relaxed) || + CheckFlag(&shared_meta()->flags, kFlagCorrupt)) { + SetCorrupt(); // Make sure all indicators are set. + return true; + } + return false; +} + +bool PersistentMemoryAllocator::IsFull() const { + return CheckFlag(&shared_meta()->flags, kFlagFull); +} + +// Dereference a block |ref| and ensure that it's valid for the desired +// |type_id| and |size|. |special| indicates that we may try to access block +// headers not available to callers but still accessed by this module. By +// having internal dereferences go through this same function, the allocator +// is hardened against corruption. +const volatile PersistentMemoryAllocator::BlockHeader* +PersistentMemoryAllocator::GetBlock(Reference ref, uint32_t type_id, + uint32_t size, bool queue_ok, + bool free_ok) const { + // Handle special cases. + if (ref == kReferenceQueue && queue_ok) + return reinterpret_cast(mem_base_ + ref); + + // Validation of parameters. + if (ref < sizeof(SharedMetadata)) + return nullptr; + if (ref % kAllocAlignment != 0) + return nullptr; + size += sizeof(BlockHeader); + if (ref + size > mem_size_) + return nullptr; + + // Validation of referenced block-header. + if (!free_ok) { + const volatile BlockHeader* const block = + reinterpret_cast(mem_base_ + ref); + if (block->cookie != kBlockCookieAllocated) + return nullptr; + if (block->size < size) + return nullptr; + if (ref + block->size > mem_size_) + return nullptr; + if (type_id != 0 && + block->type_id.load(std::memory_order_relaxed) != type_id) { + return nullptr; + } + } + + // Return pointer to block data. + return reinterpret_cast(mem_base_ + ref); +} + +void PersistentMemoryAllocator::FlushPartial(size_t length, bool sync) { + // Generally there is nothing to do as every write is done through volatile + // memory with atomic instructions to guarantee consistency. This (virtual) + // method exists so that derivced classes can do special things, such as + // tell the OS to write changes to disk now rather than when convenient. +} + +void PersistentMemoryAllocator::RecordError(int error) const { + if (errors_histogram_) + errors_histogram_->Add(error); +} + +const volatile void* PersistentMemoryAllocator::GetBlockData( + Reference ref, + uint32_t type_id, + uint32_t size) const { + DCHECK(size > 0); + const volatile BlockHeader* block = + GetBlock(ref, type_id, size, false, false); + if (!block) + return nullptr; + return reinterpret_cast(block) + sizeof(BlockHeader); +} + +void PersistentMemoryAllocator::UpdateTrackingHistograms() { + DCHECK(!readonly_); + if (used_histogram_) { + MemoryInfo meminfo; + GetMemoryInfo(&meminfo); + HistogramBase::Sample used_percent = static_cast( + ((meminfo.total - meminfo.free) * 100ULL / meminfo.total)); + used_histogram_->Add(used_percent); + } +} + + +//----- LocalPersistentMemoryAllocator ----------------------------------------- + +LocalPersistentMemoryAllocator::LocalPersistentMemoryAllocator( + size_t size, + uint64_t id, + base::StringPiece name) + : PersistentMemoryAllocator(AllocateLocalMemory(size), + size, 0, id, name, false) {} + +LocalPersistentMemoryAllocator::~LocalPersistentMemoryAllocator() { + DeallocateLocalMemory(const_cast(mem_base_), mem_size_, mem_type_); +} + +// static +PersistentMemoryAllocator::Memory +LocalPersistentMemoryAllocator::AllocateLocalMemory(size_t size) { + void* address; + +#if defined(OS_WIN) + address = + ::VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + if (address) + return Memory(address, MEM_VIRTUAL); + UmaHistogramSparse("UMA.LocalPersistentMemoryAllocator.Failures.Win", + ::GetLastError()); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + // MAP_ANON is deprecated on Linux but MAP_ANONYMOUS is not universal on Mac. + // MAP_SHARED is not available on Linux <2.4 but required on Mac. + address = ::mmap(nullptr, size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_SHARED, -1, 0); + if (address != MAP_FAILED) + return Memory(address, MEM_VIRTUAL); + UmaHistogramSparse("UMA.LocalPersistentMemoryAllocator.Failures.Posix", + errno); +#else +#error This architecture is not (yet) supported. +#endif + + // As a last resort, just allocate the memory from the heap. This will + // achieve the same basic result but the acquired memory has to be + // explicitly zeroed and thus realized immediately (i.e. all pages are + // added to the process now istead of only when first accessed). + address = malloc(size); + DPCHECK(address); + memset(address, 0, size); + return Memory(address, MEM_MALLOC); +} + +// static +void LocalPersistentMemoryAllocator::DeallocateLocalMemory(void* memory, + size_t size, + MemoryType type) { + if (type == MEM_MALLOC) { + free(memory); + return; + } + + DCHECK_EQ(MEM_VIRTUAL, type); +#if defined(OS_WIN) + BOOL success = ::VirtualFree(memory, 0, MEM_DECOMMIT); + DCHECK(success); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + int result = ::munmap(memory, size); + DCHECK_EQ(0, result); +#else +#error This architecture is not (yet) supported. +#endif +} + + +//----- SharedPersistentMemoryAllocator ---------------------------------------- + +SharedPersistentMemoryAllocator::SharedPersistentMemoryAllocator( + std::unique_ptr memory, + uint64_t id, + base::StringPiece name, + bool read_only) + : PersistentMemoryAllocator( + Memory(static_cast(memory->memory()), MEM_SHARED), + memory->mapped_size(), + 0, + id, + name, + read_only), + shared_memory_(std::move(memory)) {} + +SharedPersistentMemoryAllocator::~SharedPersistentMemoryAllocator() = default; + +// static +bool SharedPersistentMemoryAllocator::IsSharedMemoryAcceptable( + const SharedMemory& memory) { + return IsMemoryAcceptable(memory.memory(), memory.mapped_size(), 0, false); +} + + +#if !defined(OS_NACL) +//----- FilePersistentMemoryAllocator ------------------------------------------ + +FilePersistentMemoryAllocator::FilePersistentMemoryAllocator( + std::unique_ptr file, + size_t max_size, + uint64_t id, + base::StringPiece name, + bool read_only) + : PersistentMemoryAllocator( + Memory(const_cast(file->data()), MEM_FILE), + max_size != 0 ? max_size : file->length(), + 0, + id, + name, + read_only), + mapped_file_(std::move(file)) {} + +FilePersistentMemoryAllocator::~FilePersistentMemoryAllocator() = default; + +// static +bool FilePersistentMemoryAllocator::IsFileAcceptable( + const MemoryMappedFile& file, + bool read_only) { + return IsMemoryAcceptable(file.data(), file.length(), 0, read_only); +} + +void FilePersistentMemoryAllocator::FlushPartial(size_t length, bool sync) { + if (sync) + AssertBlockingAllowed(); + if (IsReadonly()) + return; + +#if defined(OS_WIN) + // Windows doesn't support asynchronous flush. + AssertBlockingAllowed(); + BOOL success = ::FlushViewOfFile(data(), length); + DPCHECK(success); +#elif defined(OS_MACOSX) + // On OSX, "invalidate" removes all cached pages, forcing a re-read from + // disk. That's not applicable to "flush" so omit it. + int result = + ::msync(const_cast(data()), length, sync ? MS_SYNC : MS_ASYNC); + DCHECK_NE(EINVAL, result); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + // On POSIX, "invalidate" forces _other_ processes to recognize what has + // been written to disk and so is applicable to "flush". + int result = ::msync(const_cast(data()), length, + MS_INVALIDATE | (sync ? MS_SYNC : MS_ASYNC)); + DCHECK_NE(EINVAL, result); +#else +#error Unsupported OS. +#endif +} +#endif // !defined(OS_NACL) + +//----- DelayedPersistentAllocation -------------------------------------------- + +// Forwarding constructors. +DelayedPersistentAllocation::DelayedPersistentAllocation( + PersistentMemoryAllocator* allocator, + subtle::Atomic32* ref, + uint32_t type, + size_t size, + bool make_iterable) + : DelayedPersistentAllocation( + allocator, + reinterpret_cast*>(ref), + type, + size, + 0, + make_iterable) {} + +DelayedPersistentAllocation::DelayedPersistentAllocation( + PersistentMemoryAllocator* allocator, + subtle::Atomic32* ref, + uint32_t type, + size_t size, + size_t offset, + bool make_iterable) + : DelayedPersistentAllocation( + allocator, + reinterpret_cast*>(ref), + type, + size, + offset, + make_iterable) {} + +DelayedPersistentAllocation::DelayedPersistentAllocation( + PersistentMemoryAllocator* allocator, + std::atomic* ref, + uint32_t type, + size_t size, + bool make_iterable) + : DelayedPersistentAllocation(allocator, + ref, + type, + size, + 0, + make_iterable) {} + +// Real constructor. +DelayedPersistentAllocation::DelayedPersistentAllocation( + PersistentMemoryAllocator* allocator, + std::atomic* ref, + uint32_t type, + size_t size, + size_t offset, + bool make_iterable) + : allocator_(allocator), + type_(type), + size_(checked_cast(size)), + offset_(checked_cast(offset)), + make_iterable_(make_iterable), + reference_(ref) { + DCHECK(allocator_); + DCHECK_NE(0U, type_); + DCHECK_LT(0U, size_); + DCHECK(reference_); +} + +DelayedPersistentAllocation::~DelayedPersistentAllocation() = default; + +void* DelayedPersistentAllocation::Get() const { + // Relaxed operations are acceptable here because it's not protecting the + // contents of the allocation in any way. + Reference ref = reference_->load(std::memory_order_acquire); + if (!ref) { + ref = allocator_->Allocate(size_, type_); + if (!ref) + return nullptr; + + // Store the new reference in its proper location using compare-and-swap. + // Use a "strong" exchange to ensure no false-negatives since the operation + // cannot be retried. + Reference existing = 0; // Must be mutable; receives actual value. + if (reference_->compare_exchange_strong(existing, ref, + std::memory_order_release, + std::memory_order_relaxed)) { + if (make_iterable_) + allocator_->MakeIterable(ref); + } else { + // Failure indicates that something else has raced ahead, performed the + // allocation, and stored its reference. Purge the allocation that was + // just done and use the other one instead. + DCHECK_EQ(type_, allocator_->GetType(existing)); + DCHECK_LE(size_, allocator_->GetAllocSize(existing)); + allocator_->ChangeType(ref, 0, type_, /*clear=*/false); + ref = existing; + } + } + + char* mem = allocator_->GetAsArray(ref, type_, size_); + if (!mem) { + // This should never happen but be tolerant if it does as corruption from + // the outside is something to guard against. + NOTREACHED(); + return nullptr; + } + return mem + offset_; +} + +} // namespace base diff --git a/native/zucchini/vendor/libchrome/base/metrics/persistent_memory_allocator.h b/native/zucchini/vendor/libchrome/base/metrics/persistent_memory_allocator.h new file mode 100644 index 0000000..978a362 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/metrics/persistent_memory_allocator.h @@ -0,0 +1,872 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_METRICS_PERSISTENT_MEMORY_ALLOCATOR_H_ +#define BASE_METRICS_PERSISTENT_MEMORY_ALLOCATOR_H_ + +#include + +#include +#include +#include + +#include "base/atomicops.h" +#include "base/base_export.h" +#include "base/files/file_path.h" +#include "base/gtest_prod_util.h" +#include "base/macros.h" +#include "base/strings/string_piece.h" + +namespace base { + +class HistogramBase; +class MemoryMappedFile; +class SharedMemory; + +// Simple allocator for pieces of a memory block that may be persistent +// to some storage or shared across multiple processes. This class resides +// under base/metrics because it was written for that purpose. It is, +// however, fully general-purpose and can be freely moved to base/memory +// if other uses are found. +// +// This class provides for thread-secure (i.e. safe against other threads +// or processes that may be compromised and thus have malicious intent) +// allocation of memory within a designated block and also a mechanism by +// which other threads can learn of these allocations. +// +// There is (currently) no way to release an allocated block of data because +// doing so would risk invalidating pointers held by other processes and +// greatly complicate the allocation algorithm. +// +// Construction of this object can accept new, clean (i.e. zeroed) memory +// or previously initialized memory. In the first case, construction must +// be allowed to complete before letting other allocators attach to the same +// segment. In other words, don't share the segment until at least one +// allocator has been attached to it. +// +// Note that memory not in active use is not accessed so it is possible to +// use virtual memory, including memory-mapped files, as backing storage with +// the OS "pinning" new (zeroed) physical RAM pages only as they are needed. +// +// OBJECTS: Although the allocator can be used in a "malloc" sense, fetching +// character arrays and manipulating that memory manually, the better way is +// generally to use the "object" methods to create and manage allocations. In +// this way the sizing, type-checking, and construction are all automatic. For +// this to work, however, every type of stored object must define two public +// "constexpr" values, kPersistentTypeId and kExpectedInstanceSize, as such: +// +// struct MyPersistentObjectType { +// // SHA1(MyPersistentObjectType): Increment this if structure changes! +// static constexpr uint32_t kPersistentTypeId = 0x3E15F6DE + 1; +// +// // Expected size for 32/64-bit check. Update this if structure changes! +// static constexpr size_t kExpectedInstanceSize = 20; +// +// ... +// }; +// +// kPersistentTypeId: This value is an arbitrary identifier that allows the +// identification of these objects in the allocator, including the ability +// to find them via iteration. The number is arbitrary but using the first +// four bytes of the SHA1 hash of the type name means that there shouldn't +// be any conflicts with other types that may also be stored in the memory. +// The fully qualified name (e.g. base::debug::MyPersistentObjectType) could +// be used to generate the hash if the type name seems common. Use a command +// like this to get the hash: echo -n "MyPersistentObjectType" | sha1sum +// If the structure layout changes, ALWAYS increment this number so that +// newer versions of the code don't try to interpret persistent data written +// by older versions with a different layout. +// +// kExpectedInstanceSize: This value is the hard-coded number that matches +// what sizeof(T) would return. By providing it explicitly, the allocator can +// verify that the structure is compatible between both 32-bit and 64-bit +// versions of the code. +// +// Using New manages the memory and then calls the default constructor for the +// object. Given that objects are persistent, no destructor is ever called +// automatically though a caller can explicitly call Delete to destruct it and +// change the type to something indicating it is no longer in use. +// +// Though persistent memory segments are transferrable between programs built +// for different natural word widths, they CANNOT be exchanged between CPUs +// of different endianess. Attempts to do so will simply see the existing data +// as corrupt and refuse to access any of it. +class BASE_EXPORT PersistentMemoryAllocator { + public: + typedef uint32_t Reference; + + // These states are used to indicate the overall condition of the memory + // segment irrespective of what is stored within it. Because the data is + // often persistent and thus needs to be readable by different versions of + // a program, these values are fixed and can never change. + enum MemoryState : uint8_t { + // Persistent memory starts all zeros and so shows "uninitialized". + MEMORY_UNINITIALIZED = 0, + + // The header has been written and the memory is ready for use. + MEMORY_INITIALIZED = 1, + + // The data should be considered deleted. This would be set when the + // allocator is being cleaned up. If file-backed, the file is likely + // to be deleted but since deletion can fail for a variety of reasons, + // having this extra status means a future reader can realize what + // should have happened. + MEMORY_DELETED = 2, + + // Outside code can create states starting with this number; these too + // must also never change between code versions. + MEMORY_USER_DEFINED = 100, + }; + + // Iterator for going through all iterable memory records in an allocator. + // Like the allocator itself, iterators are lock-free and thread-secure. + // That means that multiple threads can share an iterator and the same + // reference will not be returned twice. + // + // The order of the items returned by an iterator matches the order in which + // MakeIterable() was called on them. Once an allocation is made iterable, + // it is always such so the only possible difference between successive + // iterations is for more to be added to the end. + // + // Iteration, in general, is tolerant of corrupted memory. It will return + // what it can and stop only when corruption forces it to. Bad corruption + // could cause the same object to be returned many times but it will + // eventually quit. + class BASE_EXPORT Iterator { + public: + // Constructs an iterator on a given |allocator|, starting at the beginning. + // The allocator must live beyond the lifetime of the iterator. This class + // has read-only access to the allocator (hence "const") but the returned + // references can be used on a read/write version, too. + explicit Iterator(const PersistentMemoryAllocator* allocator); + + // As above but resuming from the |starting_after| reference. The first call + // to GetNext() will return the next object found after that reference. The + // reference must be to an "iterable" object; references to non-iterable + // objects (those that never had MakeIterable() called for them) will cause + // a run-time error. + Iterator(const PersistentMemoryAllocator* allocator, + Reference starting_after); + + // Resets the iterator back to the beginning. + void Reset(); + + // Resets the iterator, resuming from the |starting_after| reference. + void Reset(Reference starting_after); + + // Returns the previously retrieved reference, or kReferenceNull if none. + // If constructor or reset with a starting_after location, this will return + // that value. + Reference GetLast(); + + // Gets the next iterable, storing that type in |type_return|. The actual + // return value is a reference to the allocation inside the allocator or + // zero if there are no more. GetNext() may still be called again at a + // later time to retrieve any new allocations that have been added. + Reference GetNext(uint32_t* type_return); + + // Similar to above but gets the next iterable of a specific |type_match|. + // This should not be mixed with calls to GetNext() because any allocations + // skipped here due to a type mis-match will never be returned by later + // calls to GetNext() meaning it's possible to completely miss entries. + Reference GetNextOfType(uint32_t type_match); + + // As above but works using object type. + template + Reference GetNextOfType() { + return GetNextOfType(T::kPersistentTypeId); + } + + // As above but works using objects and returns null if not found. + template + const T* GetNextOfObject() { + return GetAsObject(GetNextOfType()); + } + + // Converts references to objects. This is a convenience method so that + // users of the iterator don't need to also have their own pointer to the + // allocator over which the iterator runs in order to retrieve objects. + // Because the iterator is not read/write, only "const" objects can be + // fetched. Non-const objects can be fetched using the reference on a + // non-const (external) pointer to the same allocator (or use const_cast + // to remove the qualifier). + template + const T* GetAsObject(Reference ref) const { + return allocator_->GetAsObject(ref); + } + + // Similar to GetAsObject() but converts references to arrays of things. + template + const T* GetAsArray(Reference ref, uint32_t type_id, size_t count) const { + return allocator_->GetAsArray(ref, type_id, count); + } + + // Convert a generic pointer back into a reference. A null reference will + // be returned if |memory| is not inside the persistent segment or does not + // point to an object of the specified |type_id|. + Reference GetAsReference(const void* memory, uint32_t type_id) const { + return allocator_->GetAsReference(memory, type_id); + } + + // As above but convert an object back into a reference. + template + Reference GetAsReference(const T* obj) const { + return allocator_->GetAsReference(obj); + } + + private: + // Weak-pointer to memory allocator being iterated over. + const PersistentMemoryAllocator* allocator_; + + // The last record that was returned. + std::atomic last_record_; + + // The number of records found; used for detecting loops. + std::atomic record_count_; + + DISALLOW_COPY_AND_ASSIGN(Iterator); + }; + + // Returned information about the internal state of the heap. + struct MemoryInfo { + size_t total; + size_t free; + }; + + enum : Reference { + // A common "null" reference value. + kReferenceNull = 0, + }; + + enum : uint32_t { + // A value that will match any type when doing lookups. + kTypeIdAny = 0x00000000, + + // A value indicating that the type is in transition. Work is being done + // on the contents to prepare it for a new type to come. + kTypeIdTransitioning = 0xFFFFFFFF, + }; + + enum : size_t { + kSizeAny = 1 // Constant indicating that any array size is acceptable. + }; + + // This is the standard file extension (suitable for being passed to the + // AddExtension() method of base::FilePath) for dumps of persistent memory. + static const base::FilePath::CharType kFileExtension[]; + + // The allocator operates on any arbitrary block of memory. Creation and + // persisting or sharing of that block with another process is the + // responsibility of the caller. The allocator needs to know only the + // block's |base| address, the total |size| of the block, and any internal + // |page| size (zero if not paged) across which allocations should not span. + // The |id| is an arbitrary value the caller can use to identify a + // particular memory segment. It will only be loaded during the initial + // creation of the segment and can be checked by the caller for consistency. + // The |name|, if provided, is used to distinguish histograms for this + // allocator. Only the primary owner of the segment should define this value; + // other processes can learn it from the shared state. If the underlying + // memory is |readonly| then no changes will be made to it. The resulting + // object should be stored as a "const" pointer. + // + // PersistentMemoryAllocator does NOT take ownership of the memory block. + // The caller must manage it and ensure it stays available throughout the + // lifetime of this object. + // + // Memory segments for sharing must have had an allocator attached to them + // before actually being shared. If the memory segment was just created, it + // should be zeroed before being passed here. If it was an existing segment, + // the values here will be compared to copies stored in the shared segment + // as a guard against corruption. + // + // Make sure that the memory segment is acceptable (see IsMemoryAcceptable() + // method below) before construction if the definition of the segment can + // vary in any way at run-time. Invalid memory segments will cause a crash. + PersistentMemoryAllocator(void* base, size_t size, size_t page_size, + uint64_t id, base::StringPiece name, + bool readonly); + virtual ~PersistentMemoryAllocator(); + + // Check if memory segment is acceptable for creation of an Allocator. This + // doesn't do any analysis of the data and so doesn't guarantee that the + // contents are valid, just that the paramaters won't cause the program to + // abort. The IsCorrupt() method will report detection of data problems + // found during construction and general operation. + static bool IsMemoryAcceptable(const void* data, size_t size, + size_t page_size, bool readonly); + + // Get the internal identifier for this persistent memory segment. + uint64_t Id() const; + + // Get the internal name of this allocator (possibly an empty string). + const char* Name() const; + + // Is this segment open only for read? + bool IsReadonly() const { return readonly_; } + + // Manage the saved state of the memory. + void SetMemoryState(uint8_t memory_state); + uint8_t GetMemoryState() const; + + // Create internal histograms for tracking memory use and allocation sizes + // for allocator of |name| (which can simply be the result of Name()). This + // is done seperately from construction for situations such as when the + // histograms will be backed by memory provided by this very allocator. + // + // IMPORTANT: Callers must update tools/metrics/histograms/histograms.xml + // with the following histograms: + // UMA.PersistentAllocator.name.Errors + // UMA.PersistentAllocator.name.UsedPct + void CreateTrackingHistograms(base::StringPiece name); + + // Flushes the persistent memory to any backing store. This typically does + // nothing but is used by the FilePersistentMemoryAllocator to inform the + // OS that all the data should be sent to the disk immediately. This is + // useful in the rare case where something has just been stored that needs + // to survive a hard shutdown of the machine like from a power failure. + // The |sync| parameter indicates if this call should block until the flush + // is complete but is only advisory and may or may not have an effect + // depending on the capabilities of the OS. Synchronous flushes are allowed + // only from theads that are allowed to do I/O but since |sync| is only + // advisory, all flushes should be done on IO-capable threads. + void Flush(bool sync); + + // Direct access to underlying memory segment. If the segment is shared + // across threads or processes, reading data through these values does + // not guarantee consistency. Use with care. Do not write. + const void* data() const { return const_cast(mem_base_); } + size_t length() const { return mem_size_; } + size_t size() const { return mem_size_; } + size_t used() const; + + // Get an object referenced by a |ref|. For safety reasons, the |type_id| + // code and size-of(|T|) are compared to ensure the reference is valid + // and cannot return an object outside of the memory segment. A |type_id| of + // kTypeIdAny (zero) will match any though the size is still checked. NULL is + // returned if any problem is detected, such as corrupted storage or incorrect + // parameters. Callers MUST check that the returned value is not-null EVERY + // TIME before accessing it or risk crashing! Once dereferenced, the pointer + // is safe to reuse forever. + // + // It is essential that the object be of a fixed size. All fields must be of + // a defined type that does not change based on the compiler or the CPU + // natural word size. Acceptable are char, float, double, and (u)intXX_t. + // Unacceptable are int, bool, and wchar_t which are implementation defined + // with regards to their size. + // + // Alignment must also be consistent. A uint64_t after a uint32_t will pad + // differently between 32 and 64 bit architectures. Either put the bigger + // elements first, group smaller elements into blocks the size of larger + // elements, or manually insert padding fields as appropriate for the + // largest architecture, including at the end. + // + // To protected against mistakes, all objects must have the attribute + // |kExpectedInstanceSize| (static constexpr size_t) that is a hard-coded + // numerical value -- NNN, not sizeof(T) -- that can be tested. If the + // instance size is not fixed, at least one build will fail. + // + // If the size of a structure changes, the type-ID used to recognize it + // should also change so later versions of the code don't try to read + // incompatible structures from earlier versions. + // + // NOTE: Though this method will guarantee that an object of the specified + // type can be accessed without going outside the bounds of the memory + // segment, it makes no guarantees of the validity of the data within the + // object itself. If it is expected that the contents of the segment could + // be compromised with malicious intent, the object must be hardened as well. + // + // Though the persistent data may be "volatile" if it is shared with + // other processes, such is not necessarily the case. The internal + // "volatile" designation is discarded so as to not propagate the viral + // nature of that keyword to the caller. It can add it back, if necessary, + // based on knowledge of how the allocator is being used. + template + T* GetAsObject(Reference ref) { + static_assert(std::is_standard_layout::value, "only standard objects"); + static_assert(!std::is_array::value, "use GetAsArray<>()"); + static_assert(T::kExpectedInstanceSize == sizeof(T), "inconsistent size"); + return const_cast(reinterpret_cast( + GetBlockData(ref, T::kPersistentTypeId, sizeof(T)))); + } + template + const T* GetAsObject(Reference ref) const { + static_assert(std::is_standard_layout::value, "only standard objects"); + static_assert(!std::is_array::value, "use GetAsArray<>()"); + static_assert(T::kExpectedInstanceSize == sizeof(T), "inconsistent size"); + return const_cast(reinterpret_cast( + GetBlockData(ref, T::kPersistentTypeId, sizeof(T)))); + } + + // Like GetAsObject but get an array of simple, fixed-size types. + // + // Use a |count| of the required number of array elements, or kSizeAny. + // GetAllocSize() can be used to calculate the upper bound but isn't reliable + // because padding can make space for extra elements that were not written. + // + // Remember that an array of char is a string but may not be NUL terminated. + // + // There are no compile-time or run-time checks to ensure 32/64-bit size + // compatibilty when using these accessors. Only use fixed-size types such + // as char, float, double, or (u)intXX_t. + template + T* GetAsArray(Reference ref, uint32_t type_id, size_t count) { + static_assert(std::is_fundamental::value, "use GetAsObject<>()"); + return const_cast(reinterpret_cast( + GetBlockData(ref, type_id, count * sizeof(T)))); + } + template + const T* GetAsArray(Reference ref, uint32_t type_id, size_t count) const { + static_assert(std::is_fundamental::value, "use GetAsObject<>()"); + return const_cast(reinterpret_cast( + GetBlockData(ref, type_id, count * sizeof(T)))); + } + + // Get the corresponding reference for an object held in persistent memory. + // If the |memory| is not valid or the type does not match, a kReferenceNull + // result will be returned. + Reference GetAsReference(const void* memory, uint32_t type_id) const; + + // Get the number of bytes allocated to a block. This is useful when storing + // arrays in order to validate the ending boundary. The returned value will + // include any padding added to achieve the required alignment and so could + // be larger than given in the original Allocate() request. + size_t GetAllocSize(Reference ref) const; + + // Access the internal "type" of an object. This generally isn't necessary + // but can be used to "clear" the type and so effectively mark it as deleted + // even though the memory stays valid and allocated. Changing the type is + // an atomic compare/exchange and so requires knowing the existing value. + // It will return false if the existing type is not what is expected. + // + // Changing the type doesn't mean the data is compatible with the new type. + // Passing true for |clear| will zero the memory after the type has been + // changed away from |from_type_id| but before it becomes |to_type_id| meaning + // that it is done in a manner that is thread-safe. Memory is guaranteed to + // be zeroed atomically by machine-word in a monotonically increasing order. + // + // It will likely be necessary to reconstruct the type before it can be used. + // Changing the type WILL NOT invalidate existing pointers to the data, either + // in this process or others, so changing the data structure could have + // unpredicatable results. USE WITH CARE! + uint32_t GetType(Reference ref) const; + bool ChangeType(Reference ref, + uint32_t to_type_id, + uint32_t from_type_id, + bool clear); + + // Allocated objects can be added to an internal list that can then be + // iterated over by other processes. If an allocated object can be found + // another way, such as by having its reference within a different object + // that will be made iterable, then this call is not necessary. This always + // succeeds unless corruption is detected; check IsCorrupted() to find out. + // Once an object is made iterable, its position in iteration can never + // change; new iterable objects will always be added after it in the series. + // Changing the type does not alter its "iterable" status. + void MakeIterable(Reference ref); + + // Get the information about the amount of free space in the allocator. The + // amount of free space should be treated as approximate due to extras from + // alignment and metadata. Concurrent allocations from other threads will + // also make the true amount less than what is reported. + void GetMemoryInfo(MemoryInfo* meminfo) const; + + // If there is some indication that the memory has become corrupted, + // calling this will attempt to prevent further damage by indicating to + // all processes that something is not as expected. + void SetCorrupt() const; + + // This can be called to determine if corruption has been detected in the + // segment, possibly my a malicious actor. Once detected, future allocations + // will fail and iteration may not locate all objects. + bool IsCorrupt() const; + + // Flag set if an allocation has failed because the memory segment was full. + bool IsFull() const; + + // Update those "tracking" histograms which do not get updates during regular + // operation, such as how much memory is currently used. This should be + // called before such information is to be displayed or uploaded. + void UpdateTrackingHistograms(); + + // While the above works much like malloc & free, these next methods provide + // an "object" interface similar to new and delete. + + // Reserve space in the memory segment of the desired |size| and |type_id|. + // A return value of zero indicates the allocation failed, otherwise the + // returned reference can be used by any process to get a real pointer via + // the GetAsObject() or GetAsArray calls. The actual allocated size may be + // larger and will always be a multiple of 8 bytes (64 bits). + Reference Allocate(size_t size, uint32_t type_id); + + // Allocate and construct an object in persistent memory. The type must have + // both (size_t) kExpectedInstanceSize and (uint32_t) kPersistentTypeId + // static constexpr fields that are used to ensure compatibility between + // software versions. An optional size parameter can be specified to force + // the allocation to be bigger than the size of the object; this is useful + // when the last field is actually variable length. + template + T* New(size_t size) { + if (size < sizeof(T)) + size = sizeof(T); + Reference ref = Allocate(size, T::kPersistentTypeId); + void* mem = + const_cast(GetBlockData(ref, T::kPersistentTypeId, size)); + if (!mem) + return nullptr; + DCHECK_EQ(0U, reinterpret_cast(mem) & (alignof(T) - 1)); + return new (mem) T(); + } + template + T* New() { + return New(sizeof(T)); + } + + // Similar to New, above, but construct the object out of an existing memory + // block and of an expected type. If |clear| is true, memory will be zeroed + // before construction. Though this is not standard object behavior, it + // is present to match with new allocations that always come from zeroed + // memory. Anything previously present simply ceases to exist; no destructor + // is called for it so explicitly Delete() the old object first if need be. + // Calling this will not invalidate existing pointers to the object, either + // in this process or others, so changing the object could have unpredictable + // results. USE WITH CARE! + template + T* New(Reference ref, uint32_t from_type_id, bool clear) { + DCHECK_LE(sizeof(T), GetAllocSize(ref)) << "alloc not big enough for obj"; + // Make sure the memory is appropriate. This won't be used until after + // the type is changed but checking first avoids the possibility of having + // to change the type back. + void* mem = const_cast(GetBlockData(ref, 0, sizeof(T))); + if (!mem) + return nullptr; + // Ensure the allocator's internal alignment is sufficient for this object. + // This protects against coding errors in the allocator. + DCHECK_EQ(0U, reinterpret_cast(mem) & (alignof(T) - 1)); + // Change the type, clearing the memory if so desired. The new type is + // "transitioning" so that there is no race condition with the construction + // of the object should another thread be simultaneously iterating over + // data. This will "acquire" the memory so no changes get reordered before + // it. + if (!ChangeType(ref, kTypeIdTransitioning, from_type_id, clear)) + return nullptr; + // Construct an object of the desired type on this memory, just as if + // New() had been called to create it. + T* obj = new (mem) T(); + // Finally change the type to the desired one. This will "release" all of + // the changes above and so provide a consistent view to other threads. + bool success = + ChangeType(ref, T::kPersistentTypeId, kTypeIdTransitioning, false); + DCHECK(success); + return obj; + } + + // Deletes an object by destructing it and then changing the type to a + // different value (default 0). + template + void Delete(T* obj, uint32_t new_type) { + // Get the reference for the object. + Reference ref = GetAsReference(obj); + // First change the type to "transitioning" so there is no race condition + // where another thread could find the object through iteration while it + // is been destructed. This will "acquire" the memory so no changes get + // reordered before it. It will fail if |ref| is invalid. + if (!ChangeType(ref, kTypeIdTransitioning, T::kPersistentTypeId, false)) + return; + // Destruct the object. + obj->~T(); + // Finally change the type to the desired value. This will "release" all + // the changes above. + bool success = ChangeType(ref, new_type, kTypeIdTransitioning, false); + DCHECK(success); + } + template + void Delete(T* obj) { + Delete(obj, 0); + } + + // As above but works with objects allocated from persistent memory. + template + Reference GetAsReference(const T* obj) const { + return GetAsReference(obj, T::kPersistentTypeId); + } + + // As above but works with an object allocated from persistent memory. + template + void MakeIterable(const T* obj) { + MakeIterable(GetAsReference(obj)); + } + + protected: + enum MemoryType { + MEM_EXTERNAL, + MEM_MALLOC, + MEM_VIRTUAL, + MEM_SHARED, + MEM_FILE, + }; + + struct Memory { + Memory(void* b, MemoryType t) : base(b), type(t) {} + + void* base; + MemoryType type; + }; + + // Constructs the allocator. Everything is the same as the public allocator + // except |memory| which is a structure with additional information besides + // the base address. + PersistentMemoryAllocator(Memory memory, size_t size, size_t page_size, + uint64_t id, base::StringPiece name, + bool readonly); + + // Implementation of Flush that accepts how much to flush. + virtual void FlushPartial(size_t length, bool sync); + + volatile char* const mem_base_; // Memory base. (char so sizeof guaranteed 1) + const MemoryType mem_type_; // Type of memory allocation. + const uint32_t mem_size_; // Size of entire memory segment. + const uint32_t mem_page_; // Page size allocations shouldn't cross. + + private: + struct SharedMetadata; + struct BlockHeader; + static const uint32_t kAllocAlignment; + static const Reference kReferenceQueue; + + // The shared metadata is always located at the top of the memory segment. + // These convenience functions eliminate constant casting of the base + // pointer within the code. + const SharedMetadata* shared_meta() const { + return reinterpret_cast( + const_cast(mem_base_)); + } + SharedMetadata* shared_meta() { + return reinterpret_cast(const_cast(mem_base_)); + } + + // Actual method for doing the allocation. + Reference AllocateImpl(size_t size, uint32_t type_id); + + // Get the block header associated with a specific reference. + const volatile BlockHeader* GetBlock(Reference ref, uint32_t type_id, + uint32_t size, bool queue_ok, + bool free_ok) const; + volatile BlockHeader* GetBlock(Reference ref, uint32_t type_id, uint32_t size, + bool queue_ok, bool free_ok) { + return const_cast( + const_cast(this)->GetBlock( + ref, type_id, size, queue_ok, free_ok)); + } + + // Get the actual data within a block associated with a specific reference. + const volatile void* GetBlockData(Reference ref, uint32_t type_id, + uint32_t size) const; + volatile void* GetBlockData(Reference ref, uint32_t type_id, + uint32_t size) { + return const_cast( + const_cast(this)->GetBlockData( + ref, type_id, size)); + } + + // Record an error in the internal histogram. + void RecordError(int error) const; + + const size_t vm_page_size_; // The page size used by the OS. + const bool readonly_; // Indicates access to read-only memory. + mutable std::atomic corrupt_; // Local version of "corrupted" flag. + + HistogramBase* allocs_histogram_; // Histogram recording allocs. + HistogramBase* used_histogram_; // Histogram recording used space. + HistogramBase* errors_histogram_; // Histogram recording errors. + + friend class PersistentMemoryAllocatorTest; + FRIEND_TEST_ALL_PREFIXES(PersistentMemoryAllocatorTest, AllocateAndIterate); + DISALLOW_COPY_AND_ASSIGN(PersistentMemoryAllocator); +}; + + +// This allocator uses a local memory block it allocates from the general +// heap. It is generally used when some kind of "death rattle" handler will +// save the contents to persistent storage during process shutdown. It is +// also useful for testing. +class BASE_EXPORT LocalPersistentMemoryAllocator + : public PersistentMemoryAllocator { + public: + LocalPersistentMemoryAllocator(size_t size, uint64_t id, + base::StringPiece name); + ~LocalPersistentMemoryAllocator() override; + + private: + // Allocates a block of local memory of the specified |size|, ensuring that + // the memory will not be physically allocated until accessed and will read + // as zero when that happens. + static Memory AllocateLocalMemory(size_t size); + + // Deallocates a block of local |memory| of the specified |size|. + static void DeallocateLocalMemory(void* memory, size_t size, MemoryType type); + + DISALLOW_COPY_AND_ASSIGN(LocalPersistentMemoryAllocator); +}; + + +// This allocator takes a shared-memory object and performs allocation from +// it. The memory must be previously mapped via Map() or MapAt(). The allocator +// takes ownership of the memory object. +class BASE_EXPORT SharedPersistentMemoryAllocator + : public PersistentMemoryAllocator { + public: + SharedPersistentMemoryAllocator(std::unique_ptr memory, + uint64_t id, + base::StringPiece name, + bool read_only); + ~SharedPersistentMemoryAllocator() override; + + SharedMemory* shared_memory() { return shared_memory_.get(); } + + // Ensure that the memory isn't so invalid that it would crash when passing it + // to the allocator. This doesn't guarantee the data is valid, just that it + // won't cause the program to abort. The existing IsCorrupt() call will handle + // the rest. + static bool IsSharedMemoryAcceptable(const SharedMemory& memory); + + private: + std::unique_ptr shared_memory_; + + DISALLOW_COPY_AND_ASSIGN(SharedPersistentMemoryAllocator); +}; + + +#if !defined(OS_NACL) // NACL doesn't support any kind of file access in build. +// This allocator takes a memory-mapped file object and performs allocation +// from it. The allocator takes ownership of the file object. +class BASE_EXPORT FilePersistentMemoryAllocator + : public PersistentMemoryAllocator { + public: + // A |max_size| of zero will use the length of the file as the maximum + // size. The |file| object must have been already created with sufficient + // permissions (read, read/write, or read/write/extend). + FilePersistentMemoryAllocator(std::unique_ptr file, + size_t max_size, + uint64_t id, + base::StringPiece name, + bool read_only); + ~FilePersistentMemoryAllocator() override; + + // Ensure that the file isn't so invalid that it would crash when passing it + // to the allocator. This doesn't guarantee the file is valid, just that it + // won't cause the program to abort. The existing IsCorrupt() call will handle + // the rest. + static bool IsFileAcceptable(const MemoryMappedFile& file, bool read_only); + + protected: + // PersistentMemoryAllocator: + void FlushPartial(size_t length, bool sync) override; + + private: + std::unique_ptr mapped_file_; + + DISALLOW_COPY_AND_ASSIGN(FilePersistentMemoryAllocator); +}; +#endif // !defined(OS_NACL) + +// An allocation that is defined but not executed until required at a later +// time. This allows for potential users of an allocation to be decoupled +// from the logic that defines it. In addition, there can be multiple users +// of the same allocation or any region thereof that are guaranteed to always +// use the same space. It's okay to copy/move these objects. +// +// This is a top-level class instead of an inner class of the PMA so that it +// can be forward-declared in other header files without the need to include +// the full contents of this file. +class BASE_EXPORT DelayedPersistentAllocation { + public: + using Reference = PersistentMemoryAllocator::Reference; + + // Creates a delayed allocation using the specified |allocator|. When + // needed, the memory will be allocated using the specified |type| and + // |size|. If |offset| is given, the returned pointer will be at that + // offset into the segment; this allows combining allocations into a + // single persistent segment to reduce overhead and means an "all or + // nothing" request. Note that |size| is always the total memory size + // and |offset| is just indicating the start of a block within it. If + // |make_iterable| was true, the allocation will made iterable when it + // is created; already existing allocations are not changed. + // + // Once allocated, a reference to the segment will be stored at |ref|. + // This shared location must be initialized to zero (0); it is checked + // with every Get() request to see if the allocation has already been + // done. If reading |ref| outside of this object, be sure to do an + // "acquire" load. Don't write to it -- leave that to this object. + // + // For convenience, methods taking both Atomic32 and std::atomic + // are defined. + DelayedPersistentAllocation(PersistentMemoryAllocator* allocator, + subtle::Atomic32* ref, + uint32_t type, + size_t size, + bool make_iterable); + DelayedPersistentAllocation(PersistentMemoryAllocator* allocator, + subtle::Atomic32* ref, + uint32_t type, + size_t size, + size_t offset, + bool make_iterable); + DelayedPersistentAllocation(PersistentMemoryAllocator* allocator, + std::atomic* ref, + uint32_t type, + size_t size, + bool make_iterable); + DelayedPersistentAllocation(PersistentMemoryAllocator* allocator, + std::atomic* ref, + uint32_t type, + size_t size, + size_t offset, + bool make_iterable); + ~DelayedPersistentAllocation(); + + // Gets a pointer to the defined allocation. This will realize the request + // and update the reference provided during construction. The memory will + // be zeroed the first time it is returned, after that it is shared with + // all other Get() requests and so shows any changes made to it elsewhere. + // + // If the allocation fails for any reason, null will be returned. This works + // even on "const" objects because the allocation is already defined, just + // delayed. + void* Get() const; + + // Gets the internal reference value. If this returns a non-zero value then + // a subsequent call to Get() will do nothing but convert that reference into + // a memory location -- useful for accessing an existing allocation without + // creating one unnecessarily. + Reference reference() const { + return reference_->load(std::memory_order_relaxed); + } + + private: + // The underlying object that does the actual allocation of memory. Its + // lifetime must exceed that of all DelayedPersistentAllocation objects + // that use it. + PersistentMemoryAllocator* const allocator_; + + // The desired type and size of the allocated segment plus the offset + // within it for the defined request. + const uint32_t type_; + const uint32_t size_; + const uint32_t offset_; + + // Flag indicating if allocation should be made iterable when done. + const bool make_iterable_; + + // The location at which a reference to the allocated segment is to be + // stored once the allocation is complete. If multiple delayed allocations + // share the same pointer then an allocation on one will amount to an + // allocation for all. + volatile std::atomic* const reference_; + + // No DISALLOW_COPY_AND_ASSIGN as it's okay to copy/move these objects. +}; + +} // namespace base + +#endif // BASE_METRICS_PERSISTENT_MEMORY_ALLOCATOR_H_ diff --git a/native/zucchini/vendor/libchrome/base/metrics/sparse_histogram.h b/native/zucchini/vendor/libchrome/base/metrics/sparse_histogram.h new file mode 100644 index 0000000..913762c --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/metrics/sparse_histogram.h @@ -0,0 +1,108 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_METRICS_SPARSE_HISTOGRAM_H_ +#define BASE_METRICS_SPARSE_HISTOGRAM_H_ + +#include +#include + +#include +#include +#include + +#include "base/base_export.h" +#include "base/macros.h" +#include "base/metrics/histogram_base.h" +#include "base/metrics/histogram_samples.h" +#include "base/synchronization/lock.h" + +namespace base { + +class HistogramSamples; +class PersistentHistogramAllocator; +class Pickle; +class PickleIterator; + +class BASE_EXPORT SparseHistogram : public HistogramBase { + public: + // If there's one with same name, return the existing one. If not, create a + // new one. + static HistogramBase* FactoryGet(const std::string& name, int32_t flags); + + // Create a histogram using data in persistent storage. The allocator must + // live longer than the created sparse histogram. + static std::unique_ptr PersistentCreate( + PersistentHistogramAllocator* allocator, + const char* name, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta); + + ~SparseHistogram() override; + + // HistogramBase implementation: + uint64_t name_hash() const override; + HistogramType GetHistogramType() const override; + bool HasConstructionArguments(Sample expected_minimum, + Sample expected_maximum, + uint32_t expected_bucket_count) const override; + void Add(Sample value) override; + void AddCount(Sample value, int count) override; + void AddSamples(const HistogramSamples& samples) override; + bool AddSamplesFromPickle(base::PickleIterator* iter) override; + std::unique_ptr SnapshotSamples() const override; + std::unique_ptr SnapshotDelta() override; + std::unique_ptr SnapshotFinalDelta() const override; + void WriteHTMLGraph(std::string* output) const override; + void WriteAscii(std::string* output) const override; + + protected: + // HistogramBase implementation: + void SerializeInfoImpl(base::Pickle* pickle) const override; + + private: + // Clients should always use FactoryGet to create SparseHistogram. + explicit SparseHistogram(const char* name); + + SparseHistogram(PersistentHistogramAllocator* allocator, + const char* name, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta); + + friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo( + base::PickleIterator* iter); + static HistogramBase* DeserializeInfoImpl(base::PickleIterator* iter); + + void GetParameters(DictionaryValue* params) const override; + void GetCountAndBucketData(Count* count, + int64_t* sum, + ListValue* buckets) const override; + + // Helpers for emitting Ascii graphic. Each method appends data to output. + void WriteAsciiImpl(bool graph_it, + const std::string& newline, + std::string* output) const; + + // Write a common header message describing this histogram. + void WriteAsciiHeader(const Count total_count, + std::string* output) const; + + // For constuctor calling. + friend class SparseHistogramTest; + + // Protects access to |samples_|. + mutable base::Lock lock_; + + // Flag to indicate if PrepareFinalDelta has been previously called. + mutable bool final_delta_created_ = false; + + std::unique_ptr unlogged_samples_; + std::unique_ptr logged_samples_; + + DISALLOW_COPY_AND_ASSIGN(SparseHistogram); +}; + +} // namespace base + +#endif // BASE_METRICS_SPARSE_HISTOGRAM_H_ diff --git a/native/zucchini/vendor/libchrome/base/no_destructor.h b/native/zucchini/vendor/libchrome/base/no_destructor.h new file mode 100644 index 0000000..aabc6e6 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/no_destructor.h @@ -0,0 +1,99 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_NO_DESTRUCTOR_H_ +#define BASE_NO_DESTRUCTOR_H_ + +#include +#include + +namespace base { + +// A wrapper that makes it easy to create an object of type T with static +// storage duration that: +// - is only constructed on first access +// - never invokes the destructor +// in order to satisfy the styleguide ban on global constructors and +// destructors. +// +// Runtime constant example: +// const std::string& GetLineSeparator() { +// // Forwards to std::string(size_t, char, const Allocator&) constructor. +// static const base::NoDestructor s(5, '-'); +// return *s; +// } +// +// More complex initialization with a lambda: +// const std::string& GetSessionNonce() { +// static const base::NoDestructor nonce([] { +// std::string s(16); +// crypto::RandString(s.data(), s.size()); +// return s; +// }()); +// return *nonce; +// } +// +// NoDestructor stores the object inline, so it also avoids a pointer +// indirection and a malloc. Also note that since C++11 static local variable +// initialization is thread-safe and so is this pattern. Code should prefer to +// use NoDestructor over: +// - The CR_DEFINE_STATIC_LOCAL() helper macro. +// - A function scoped static T* or T& that is dynamically initialized. +// - A global base::LazyInstance. +// +// Note that since the destructor is never run, this *will* leak memory if used +// as a stack or member variable. Furthermore, a NoDestructor should never +// have global scope as that may require a static initializer. +template +class NoDestructor { + public: + // Not constexpr; just write static constexpr T x = ...; if the value should + // be a constexpr. + template + explicit NoDestructor(Args&&... args) { + new (storage_) T(std::forward(args)...); + } + + // Allows copy and move construction of the contained type, to allow + // construction from an initializer list, e.g. for std::vector. + explicit NoDestructor(const T& x) { new (storage_) T(x); } + explicit NoDestructor(T&& x) { new (storage_) T(std::move(x)); } + + NoDestructor(const NoDestructor&) = delete; + NoDestructor& operator=(const NoDestructor&) = delete; + + ~NoDestructor() = default; + + const T& operator*() const { return *get(); } + T& operator*() { return *get(); } + + const T* operator->() const { return get(); } + T* operator->() { return get(); } + + const T* get() const { return reinterpret_cast(storage_); } + T* get() { return reinterpret_cast(storage_); } + + private: + alignas(T) char storage_[sizeof(T)]; + +#if defined(LEAK_SANITIZER) + // TODO(https://crbug.com/812277): This is a hack to work around the fact + // that LSan doesn't seem to treat NoDestructor as a root for reachability + // analysis. This means that code like this: + // static base::NoDestructor> v({1, 2, 3}); + // is considered a leak. Using the standard leak sanitizer annotations to + // suppress leaks doesn't work: std::vector is implicitly constructed before + // calling the base::NoDestructor constructor. + // + // Unfortunately, I haven't been able to demonstrate this issue in simpler + // reproductions: until that's resolved, hold an explicit pointer to the + // placement-new'd object in leak sanitizer mode to help LSan realize that + // objects allocated by the contained type are still reachable. + T* storage_ptr_ = reinterpret_cast(storage_); +#endif // defined(LEAK_SANITIZER) +}; + +} // namespace base + +#endif // BASE_NO_DESTRUCTOR_H_ diff --git a/native/zucchini/vendor/libchrome/base/numerics/checked_math.h b/native/zucchini/vendor/libchrome/base/numerics/checked_math.h new file mode 100644 index 0000000..ede3344 --- /dev/null +++ b/native/zucchini/vendor/libchrome/base/numerics/checked_math.h @@ -0,0 +1,393 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_NUMERICS_CHECKED_MATH_H_ +#define BASE_NUMERICS_CHECKED_MATH_H_ + +#include + +#include +#include + +#include "base/numerics/checked_math_impl.h" + +namespace base { +namespace internal { + +template +class CheckedNumeric { + static_assert(std::is_arithmetic::value, + "CheckedNumeric: T must be a numeric type."); + + public: + using type = T; + + constexpr CheckedNumeric() = default; + + // Copy constructor. + template + constexpr CheckedNumeric(const CheckedNumeric& rhs) + : state_(rhs.state_.value(), rhs.IsValid()) {} + + template + friend class CheckedNumeric; + + // This is not an explicit constructor because we implicitly upgrade regular + // numerics to CheckedNumerics to make them easier to use. + template + constexpr CheckedNumeric(Src value) // NOLINT(runtime/explicit) + : state_(value) { + static_assert(std::is_arithmetic::value, "Argument must be numeric."); + } + + // This is not an explicit constructor because we want a seamless conversion + // from StrictNumeric types. + template + constexpr CheckedNumeric( + StrictNumeric value) // NOLINT(runtime/explicit) + : state_(static_cast(value)) {} + + // IsValid() - The public API to test if a CheckedNumeric is currently valid. + // A range checked destination type can be supplied using the Dst template + // parameter. + template + constexpr bool IsValid() const { + return state_.is_valid() && + IsValueInRangeForNumericType(state_.value()); + } + + // AssignIfValid(Dst) - Assigns the underlying value if it is currently valid + // and is within the range supported by the destination type. Returns true if + // successful and false otherwise. + template +#if defined(__clang__) || defined(__GNUC__) + __attribute__((warn_unused_result)) +#elif defined(_MSC_VER) + _Check_return_ +#endif + constexpr bool + AssignIfValid(Dst* result) const { + return BASE_NUMERICS_LIKELY(IsValid()) + ? ((*result = static_cast(state_.value())), true) + : false; + } + + // ValueOrDie() - The primary accessor for the underlying value. If the + // current state is not valid it will CHECK and crash. + // A range checked destination type can be supplied using the Dst template + // parameter, which will trigger a CHECK if the value is not in bounds for + // the destination. + // The CHECK behavior can be overridden by supplying a handler as a + // template parameter, for test code, etc. However, the handler cannot access + // the underlying value, and it is not available through other means. + template + constexpr StrictNumeric ValueOrDie() const { + return BASE_NUMERICS_LIKELY(IsValid()) + ? static_cast(state_.value()) + : CheckHandler::template HandleFailure(); + } + + // ValueOrDefault(T default_value) - A convenience method that returns the + // current value if the state is valid, and the supplied default_value for + // any other state. + // A range checked destination type can be supplied using the Dst template + // parameter. WARNING: This function may fail to compile or CHECK at runtime + // if the supplied default_value is not within range of the destination type. + template + constexpr StrictNumeric ValueOrDefault(const Src default_value) const { + return BASE_NUMERICS_LIKELY(IsValid()) + ? static_cast(state_.value()) + : checked_cast(default_value); + } + + // Returns a checked numeric of the specified type, cast from the current + // CheckedNumeric. If the current state is invalid or the destination cannot + // represent the result then the returned CheckedNumeric will be invalid. + template + constexpr CheckedNumeric::type> Cast() const { + return *this; + } + + // This friend method is available solely for providing more detailed logging + // in the the tests. Do not implement it in production code, because the + // underlying values may change at any time. + template + friend U GetNumericValueForTest(const CheckedNumeric& src); + + // Prototypes for the supported arithmetic operator overloads. + template + constexpr CheckedNumeric& operator+=(const Src rhs); + template + constexpr CheckedNumeric& operator-=(const Src rhs); + template + constexpr CheckedNumeric& operator*=(const Src rhs); + template + constexpr CheckedNumeric& operator/=(const Src rhs); + template + constexpr CheckedNumeric& operator%=(const Src rhs); + template + constexpr CheckedNumeric& operator<<=(const Src rhs); + template + constexpr CheckedNumeric& operator>>=(const Src rhs); + template + constexpr CheckedNumeric& operator&=(const Src rhs); + template + constexpr CheckedNumeric& operator|=(const Src rhs); + template + constexpr CheckedNumeric& operator^=(const Src rhs); + + constexpr CheckedNumeric operator-() const { + // The negation of two's complement int min is int min, so we simply + // check for that in the constexpr case. + // We use an optimized code path for a known run-time variable. + return MustTreatAsConstexpr(state_.value()) || !std::is_signed::value || + std::is_floating_point::value + ? CheckedNumeric( + NegateWrapper(state_.value()), + IsValid() && (!std::is_signed::value || + std::is_floating_point::value || + NegateWrapper(state_.value()) != + std::numeric_limits::lowest())) + : FastRuntimeNegate(); + } + + constexpr CheckedNumeric operator~() const { + return CheckedNumeric( + InvertWrapper(state_.value()), IsValid()); + } + + constexpr CheckedNumeric Abs() const { + return !IsValueNegative(state_.value()) ? *this : -*this; + } + + template + constexpr CheckedNumeric::type> Max( + const U rhs) const { + using R = typename UnderlyingType::type; + using result_type = typename MathWrapper::type; + // TODO(jschuh): This can be converted to the MathOp version and remain + // constexpr once we have C++14 support. + return CheckedNumeric( + static_cast( + IsGreater::Test(state_.value(), Wrapper::value(rhs)) + ? state_.value() + : Wrapper::value(rhs)), + state_.is_valid() && Wrapper::is_valid(rhs)); + } + + template + constexpr CheckedNumeric::type> Min( + const U rhs) const { + using R = typename UnderlyingType::type; + using result_type = typename MathWrapper::type; + // TODO(jschuh): This can be converted to the MathOp version and remain + // constexpr once we have C++14 support. + return CheckedNumeric( + static_cast( + IsLess::Test(state_.value(), Wrapper::value(rhs)) + ? state_.value() + : Wrapper::value(rhs)), + state_.is_valid() && Wrapper::is_valid(rhs)); + } + + // This function is available only for integral types. It returns an unsigned + // integer of the same width as the source type, containing the absolute value + // of the source, and properly handling signed min. + constexpr CheckedNumeric::type> + UnsignedAbs() const { + return CheckedNumeric::type>( + SafeUnsignedAbs(state_.value()), state_.is_valid()); + } + + constexpr CheckedNumeric& operator++() { + *this += 1; + return *this; + } + + constexpr CheckedNumeric operator++(int) { + CheckedNumeric value = *this; + *this += 1; + return value; + } + + constexpr CheckedNumeric& operator--() { + *this -= 1; + return *this; + } + + constexpr CheckedNumeric operator--(int) { + CheckedNumeric value = *this; + *this -= 1; + return value; + } + + // These perform the actual math operations on the CheckedNumerics. + // Binary arithmetic operations. + template