mirror of
https://github.com/facebook/sapling.git
synced 2024-10-06 23:07:18 +03:00
edenfs-client: use clidispatch IO and Error types
Summary: This simplifies the error handling and makes it more compatible with things that might capture the output, including the Python testing framework. Reviewed By: DurhamG Differential Revision: D19325642 fbshipit-source-id: 53de8b9a8294219e2b8e62831dce236841bd4cbb
This commit is contained in:
parent
12eaf82291
commit
3895af590a
@ -7,6 +7,7 @@ edition = "2018"
|
|||||||
anyhow = "*"
|
anyhow = "*"
|
||||||
byteorder = "1.2"
|
byteorder = "1.2"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
|
clidispatch = { path = "../clidispatch" }
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
rust-crypto = "0.2"
|
rust-crypto = "0.2"
|
||||||
thrift-types = { path = "../thrift-types" }
|
thrift-types = { path = "../thrift-types" }
|
||||||
|
@ -8,8 +8,9 @@
|
|||||||
use thrift_types::edenfs as eden;
|
use thrift_types::edenfs as eden;
|
||||||
|
|
||||||
use crate::path_relativizer::PathRelativizer;
|
use crate::path_relativizer::PathRelativizer;
|
||||||
use anyhow::{Error, Result};
|
use anyhow::{bail, ensure, Error, Result};
|
||||||
use byteorder::{BigEndian, ByteOrder};
|
use byteorder::{BigEndian, ByteOrder};
|
||||||
|
use clidispatch::{errors::FallbackToPython, io::IO};
|
||||||
use crypto::{digest::Digest, sha2::Sha256};
|
use crypto::{digest::Digest, sha2::Sha256};
|
||||||
use eden::client::EdenService;
|
use eden::client::EdenService;
|
||||||
use eden::{GetScmStatusParams, GetScmStatusResult, ScmFileStatus, ScmStatus};
|
use eden::{GetScmStatusParams, GetScmStatusResult, ScmFileStatus, ScmStatus};
|
||||||
@ -38,12 +39,11 @@ use thrift_types::fbthrift::binary_protocol::BinaryProtocol;
|
|||||||
use thrift_types::fbthrift::ApplicationExceptionErrorCode;
|
use thrift_types::fbthrift::ApplicationExceptionErrorCode;
|
||||||
use thrift_types::futures::future::TryFutureExt;
|
use thrift_types::futures::future::TryFutureExt;
|
||||||
use tokio_core::reactor::Core;
|
use tokio_core::reactor::Core;
|
||||||
|
#[cfg(unix)]
|
||||||
use tokio_uds::UnixStream;
|
use tokio_uds::UnixStream;
|
||||||
|
|
||||||
/// Standalone status command for edenfs.
|
/// Standalone status command for edenfs.
|
||||||
///
|
///
|
||||||
/// Return `None` if this implementation cannot handle the `status` command.
|
|
||||||
///
|
|
||||||
/// TODO: This does not match the Python implementation. Namely:
|
/// TODO: This does not match the Python implementation. Namely:
|
||||||
/// - No pager.
|
/// - No pager.
|
||||||
/// - Colors are hard-coded here.
|
/// - Colors are hard-coded here.
|
||||||
@ -61,7 +61,28 @@ pub fn maybe_status_fastpath(
|
|||||||
repo_root: &Path,
|
repo_root: &Path,
|
||||||
cwd: &Path,
|
cwd: &Path,
|
||||||
print_config: PrintConfig,
|
print_config: PrintConfig,
|
||||||
) -> Option<Result<u8>> {
|
io: &mut IO,
|
||||||
|
) -> Result<u8> {
|
||||||
|
maybe_status_fastpath_internal(repo_root, cwd, print_config, io)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn maybe_status_fastpath_internal(
|
||||||
|
repo_root: &Path,
|
||||||
|
cwd: &Path,
|
||||||
|
print_config: PrintConfig,
|
||||||
|
io: &mut IO,
|
||||||
|
) -> Result<u8> {
|
||||||
|
Err(FallbackToPython.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn maybe_status_fastpath_internal(
|
||||||
|
repo_root: &Path,
|
||||||
|
cwd: &Path,
|
||||||
|
print_config: PrintConfig,
|
||||||
|
io: &mut IO,
|
||||||
|
) -> Result<u8> {
|
||||||
let mut core = Core::new().expect("Core creation failed");
|
let mut core = Core::new().expect("Core creation failed");
|
||||||
let handle = core.handle();
|
let handle = core.handle();
|
||||||
|
|
||||||
@ -70,56 +91,26 @@ pub fn maybe_status_fastpath(
|
|||||||
// of the Eden mount has been bind mounted to another location, resulting
|
// of the Eden mount has been bind mounted to another location, resulting
|
||||||
// in the Eden mount appearing at multiple separate locations.
|
// in the Eden mount appearing at multiple separate locations.
|
||||||
let eden_root = repo_root.join(".eden").join("root");
|
let eden_root = repo_root.join(".eden").join("root");
|
||||||
let eden_root = match read_link(eden_root) {
|
let eden_root = read_link(eden_root).map_err(|_| FallbackToPython)?;
|
||||||
Ok(eden_root) => eden_root.into_os_string().into_string(),
|
let eden_root = eden_root
|
||||||
// does not exist or not a symlink - therefore this is not an Eden repo
|
.into_os_string()
|
||||||
Err(_) => return None,
|
.into_string()
|
||||||
};
|
.map_err(|_| FallbackToPython)?;
|
||||||
let eden_root = match eden_root {
|
|
||||||
Ok(eden_root) => eden_root,
|
|
||||||
// Repo root is not valid UTF-8. This is not normally expected.
|
|
||||||
// (We perhaps could just change the thrift API to treat the path as
|
|
||||||
// binary so we can deal with this situation more easily here.)
|
|
||||||
Err(_) => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Look up Eden's socket address.
|
// Look up Eden's socket address.
|
||||||
let sock_addr = repo_root.join(".eden").join("socket");
|
let sock_addr = repo_root.join(".eden").join("socket");
|
||||||
let sock_addr = match read_link(sock_addr) {
|
let sock_addr = read_link(sock_addr).map_err(|_| FallbackToPython)?;
|
||||||
// does not exist or not a symlink - therefore this is not an Eden repo
|
let sock = UnixStream::connect(&sock_addr, &handle).map_err(|_| FallbackToPython)?;
|
||||||
Ok(sock_addr) => sock_addr,
|
|
||||||
Err(_) => return None,
|
|
||||||
};
|
|
||||||
let sock = match UnixStream::connect(&sock_addr, &handle) {
|
|
||||||
Ok(sock) => sock,
|
|
||||||
// cannot connect to eden daemon or invalid unix domain socket
|
|
||||||
Err(_) => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let transport = SocketTransport::new(&handle, sock);
|
let transport = SocketTransport::new(&handle, sock);
|
||||||
let client = EdenService::new(BinaryProtocol, transport);
|
let client = EdenService::new(BinaryProtocol, transport);
|
||||||
|
let sock2 = UnixStream::connect(sock_addr, &handle).map_err(|_| FallbackToPython)?;
|
||||||
let sock2 = match UnixStream::connect(sock_addr, &handle) {
|
|
||||||
Ok(sock) => sock,
|
|
||||||
// cannot connect to eden daemon or invalid unix domain socket
|
|
||||||
Err(_) => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let transport = SocketTransport::new(&handle, sock2);
|
let transport = SocketTransport::new(&handle, sock2);
|
||||||
let fb303_client = BaseService::new(BinaryProtocol, transport);
|
let fb303_client = BaseService::new(BinaryProtocol, transport);
|
||||||
|
|
||||||
// TODO(mbolin): Run read_hg_dirstate() and core.run() in parallel.
|
// TODO(mbolin): Run read_hg_dirstate() and core.run() in parallel.
|
||||||
let dirstate_data = match read_hg_dirstate(&repo_root) {
|
let dirstate_data = read_hg_dirstate(&repo_root)?;
|
||||||
Ok(result) => result,
|
|
||||||
Err(error) => {
|
|
||||||
eprintln!(
|
|
||||||
"error reading {}/.hg/dirstate: {}",
|
|
||||||
repo_root.display(),
|
|
||||||
error
|
|
||||||
);
|
|
||||||
return Some(Ok(1));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// If any of the files are present that should trigger the 'morestatus' extension, bail out of
|
// If any of the files are present that should trigger the 'morestatus' extension, bail out of
|
||||||
// the wrapper here and default to the Python implementation. D9025269 has a prototype
|
// the wrapper here and default to the Python implementation. D9025269 has a prototype
|
||||||
@ -127,7 +118,7 @@ pub fn maybe_status_fastpath(
|
|||||||
// and call out to it here rather than maintain a parallel implementation in the wrapper.
|
// and call out to it here rather than maintain a parallel implementation in the wrapper.
|
||||||
let hg_dir = repo_root.join(".hg");
|
let hg_dir = repo_root.join(".hg");
|
||||||
if needs_morestatus_extension(&hg_dir, &dirstate_data.p2) {
|
if needs_morestatus_extension(&hg_dir, &dirstate_data.p2) {
|
||||||
return None;
|
return Err(FallbackToPython.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let stdout = io::stdout();
|
let stdout = io::stdout();
|
||||||
@ -140,55 +131,39 @@ pub fn maybe_status_fastpath(
|
|||||||
&eden_root,
|
&eden_root,
|
||||||
dirstate_data.p1,
|
dirstate_data.p1,
|
||||||
print_config.status_types.ignored,
|
print_config.status_types.ignored,
|
||||||
);
|
)?;
|
||||||
|
|
||||||
let result = match &status {
|
let relativizer = PathRelativizer::new(cwd.to_path_buf(), repo_root.to_path_buf());
|
||||||
Ok(status) => {
|
let relativizer = HgStatusPathRelativizer::new(print_config.root_relative, relativizer);
|
||||||
let mut locked_stdout = stdout.lock();
|
print_config.print_status(
|
||||||
let relativizer = PathRelativizer::new(cwd.to_path_buf(), repo_root.to_path_buf());
|
&repo_root,
|
||||||
let relativizer = HgStatusPathRelativizer::new(print_config.root_relative, relativizer);
|
&status.status,
|
||||||
if let Err(error) = print_config.print_status(
|
&dirstate_data,
|
||||||
&repo_root,
|
&relativizer,
|
||||||
&status.status,
|
use_color,
|
||||||
&dirstate_data,
|
&mut io.output,
|
||||||
&relativizer,
|
)?;
|
||||||
use_color,
|
|
||||||
&mut locked_stdout,
|
|
||||||
) {
|
|
||||||
eprintln!("error writing to stdout: {}", error);
|
|
||||||
1
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(error) => {
|
|
||||||
// We could fall back on hg if the RPC fails, but it's likely to fail with the same
|
|
||||||
// error.
|
|
||||||
eprintln!("error fetching eden status: {}", error);
|
|
||||||
1
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Ok(version) = status.and_then(|s| s.version.parse::<u32>().map_err(|e| e.into())) {
|
if let Ok(version) = status.version.parse::<u32>() {
|
||||||
if use_color {
|
if use_color {
|
||||||
eprint!("{}", BOLD);
|
let _ = io.write_err(BOLD);
|
||||||
}
|
}
|
||||||
// TODO: in the future we can have this look at some configuration that
|
// TODO: in the future we can have this look at some configuration that
|
||||||
// we ship with the eden server, but for now, let's just hard code the
|
// we ship with the eden server, but for now, let's just hard code the
|
||||||
// version check and advice.
|
// version check and advice.
|
||||||
if version < 20180825 {
|
if version < 20180825 {
|
||||||
eprintln!(
|
let _ = io.write_err(
|
||||||
"
|
"
|
||||||
IMPORTANT: Your running Eden server version is known to have issues importing
|
IMPORTANT: Your running Eden server version is known to have issues importing
|
||||||
data from mercurial. You should run `eden restart` at your earliest opportunity
|
data from mercurial. You should run `eden restart` at your earliest opportunity
|
||||||
to pick up the fix."
|
to pick up the fix.\n",
|
||||||
);
|
);
|
||||||
} else if version == 20181023 {
|
} else if version == 20181023 {
|
||||||
eprintln!(
|
let _ = io.write_err(
|
||||||
"
|
"
|
||||||
IMPORTANT: Your running Eden server version is known to have issues importing
|
IMPORTANT: Your running Eden server version is known to have issues importing
|
||||||
data from mercurial. You should run `eden restart && eden gc` at your earliest
|
data from mercurial. You should run `eden restart && eden gc` at your earliest
|
||||||
opportunity to pick up the fix and fixup the cache."
|
opportunity to pick up the fix and fixup the cache.\n",
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
use chrono::offset::TimeZone;
|
use chrono::offset::TimeZone;
|
||||||
@ -201,19 +176,19 @@ opportunity to pick up the fix and fixup the cache."
|
|||||||
let version_date = chrono::Local.ymd(year as i32, month, day);
|
let version_date = chrono::Local.ymd(year as i32, month, day);
|
||||||
|
|
||||||
if today - version_date > chrono::Duration::days(45) {
|
if today - version_date > chrono::Duration::days(45) {
|
||||||
eprintln!(
|
let _ = io.write_err(
|
||||||
"
|
"
|
||||||
Your running Eden server is more than 45 days old. You should run
|
Your running Eden server is more than 45 days old. You should run
|
||||||
`eden restart` to update to the current release."
|
`eden restart` to update to the current release.\n",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if use_color {
|
if use_color {
|
||||||
eprint!("{}", RESET);
|
let _ = io.write_err(RESET);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Ok(result))
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
const NULL_COMMIT: [u8; 20] = [0; 20];
|
const NULL_COMMIT: [u8; 20] = [0; 20];
|
||||||
@ -454,8 +429,8 @@ impl PrintConfig {
|
|||||||
relativizer: &HgStatusPathRelativizer,
|
relativizer: &HgStatusPathRelativizer,
|
||||||
use_color: bool,
|
use_color: bool,
|
||||||
out: &mut W,
|
out: &mut W,
|
||||||
) -> Result<(), io::Error> {
|
) -> Result<()> {
|
||||||
let groups = group_entries(&repo_root, &status, &dirstate_data);
|
let groups = group_entries(&repo_root, &status, &dirstate_data)?;
|
||||||
let endl = self.endl;
|
let endl = self.endl;
|
||||||
|
|
||||||
let mut print_group =
|
let mut print_group =
|
||||||
@ -568,12 +543,12 @@ fn group_entries(
|
|||||||
repo_root: &Path,
|
repo_root: &Path,
|
||||||
status: &ScmStatus,
|
status: &ScmStatus,
|
||||||
dirstate_data: &DirstateData,
|
dirstate_data: &DirstateData,
|
||||||
) -> GroupedEntries {
|
) -> Result<GroupedEntries> {
|
||||||
let mut result = GroupedEntries::default();
|
let mut result = GroupedEntries::default();
|
||||||
let mut dirstates = dirstate_data.tuples.clone();
|
let mut dirstates = dirstate_data.tuples.clone();
|
||||||
for (path_str, status_code) in &status.entries {
|
for (path_str, status_code) in &status.entries {
|
||||||
let path = PathBuf::from(OsString::from_vec(path_str.to_vec()));
|
let path = Path::new(str::from_utf8(path_str)?);
|
||||||
let dirstate = dirstates.remove(&path);
|
let dirstate = dirstates.remove(path);
|
||||||
use self::DirstateDataStatus::*;
|
use self::DirstateDataStatus::*;
|
||||||
let group = match (status_code.clone(), dirstate) {
|
let group = match (status_code.clone(), dirstate) {
|
||||||
(ScmFileStatus::MODIFIED, Some(DirstateDataTuple { status: Remove, .. })) => {
|
(ScmFileStatus::MODIFIED, Some(DirstateDataTuple { status: Remove, .. })) => {
|
||||||
@ -607,7 +582,7 @@ fn group_entries(
|
|||||||
once Thrift enums are translated as Rust enums."
|
once Thrift enums are translated as Rust enums."
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
group.push(path);
|
group.push(path.to_path_buf());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (path, tuple) in dirstates {
|
for (path, tuple) in dirstates {
|
||||||
@ -637,7 +612,7 @@ fn group_entries(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DirstateReader {
|
struct DirstateReader {
|
||||||
@ -670,17 +645,14 @@ impl DirstateReader {
|
|||||||
Ok(BigEndian::read_u32(&buf))
|
Ok(BigEndian::read_u32(&buf))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
fn read_path(&mut self) -> Result<PathBuf> {
|
||||||
fn read_path(&mut self) -> Result<PathBuf, io::Error> {
|
|
||||||
let path_length = self.read_u16()?;
|
let path_length = self.read_u16()?;
|
||||||
|
|
||||||
let mut buf = vec![0; path_length as usize];
|
let mut buf = vec![0; path_length as usize];
|
||||||
self.reader.read_exact(&mut buf)?;
|
self.reader.read_exact(&mut buf)?;
|
||||||
self.sha256.input(&buf);
|
self.sha256.input(&buf);
|
||||||
|
|
||||||
use std::ffi::OsStr;
|
Ok(Path::new(str::from_utf8(&buf)?).to_path_buf())
|
||||||
use std::os::unix::ffi::OsStrExt;
|
|
||||||
Ok(OsStr::from_bytes(&buf).into())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_checksum(&mut self) -> Result<(), io::Error> {
|
fn verify_checksum(&mut self) -> Result<(), io::Error> {
|
||||||
@ -712,7 +684,7 @@ impl DirstateReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_hg_dirstate(repo_root: &Path) -> Result<DirstateData, io::Error> {
|
fn read_hg_dirstate(repo_root: &Path) -> Result<DirstateData> {
|
||||||
let dirstate = repo_root.join(".hg").join("dirstate");
|
let dirstate = repo_root.join(".hg").join("dirstate");
|
||||||
let mut reader = DirstateReader {
|
let mut reader = DirstateReader {
|
||||||
reader: BufReader::new(File::open(dirstate)?),
|
reader: BufReader::new(File::open(dirstate)?),
|
||||||
@ -725,12 +697,7 @@ fn read_hg_dirstate(repo_root: &Path) -> Result<DirstateData, io::Error> {
|
|||||||
reader.hashing_read(&mut p2)?;
|
reader.hashing_read(&mut p2)?;
|
||||||
|
|
||||||
let version = reader.read_u32()?;
|
let version = reader.read_u32()?;
|
||||||
if version != 1 {
|
ensure!(version == 1, "Unsupported dirstate version: {}", version);
|
||||||
return Err(io::Error::new(
|
|
||||||
io::ErrorKind::InvalidInput,
|
|
||||||
format!("Unsupported dirstate version: {}", version),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut tuples: HashMap<PathBuf, DirstateDataTuple> = HashMap::new();
|
let mut tuples: HashMap<PathBuf, DirstateDataTuple> = HashMap::new();
|
||||||
let mut copymap: HashMap<PathBuf, PathBuf> = HashMap::new();
|
let mut copymap: HashMap<PathBuf, PathBuf> = HashMap::new();
|
||||||
@ -745,12 +712,7 @@ fn read_hg_dirstate(repo_root: &Path) -> Result<DirstateData, io::Error> {
|
|||||||
b'r' => DirstateDataStatus::Remove,
|
b'r' => DirstateDataStatus::Remove,
|
||||||
b'a' => DirstateDataStatus::Add,
|
b'a' => DirstateDataStatus::Add,
|
||||||
b'?' => DirstateDataStatus::Unknown,
|
b'?' => DirstateDataStatus::Unknown,
|
||||||
value => {
|
value => bail!("Unknown status type (ASCII value): {}", value),
|
||||||
return Err(io::Error::new(
|
|
||||||
io::ErrorKind::InvalidInput,
|
|
||||||
format!("Unknown status type (ASCII value): {}", value),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// The next four bytes compose an unsigned integer that corresponds to mode_t.
|
// The next four bytes compose an unsigned integer that corresponds to mode_t.
|
||||||
@ -762,12 +724,7 @@ fn read_hg_dirstate(repo_root: &Path) -> Result<DirstateData, io::Error> {
|
|||||||
0 => DirstateMergeState::NotApplicable,
|
0 => DirstateMergeState::NotApplicable,
|
||||||
-1 => DirstateMergeState::BothParents,
|
-1 => DirstateMergeState::BothParents,
|
||||||
-2 => DirstateMergeState::OtherParent,
|
-2 => DirstateMergeState::OtherParent,
|
||||||
value => {
|
value => bail!("Unknown merge type: {}", value),
|
||||||
return Err(io::Error::new(
|
|
||||||
io::ErrorKind::InvalidInput,
|
|
||||||
format!("Unknown merge type: {}", value),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let path = reader.read_path()?;
|
let path = reader.read_path()?;
|
||||||
tuples.insert(
|
tuples.insert(
|
||||||
@ -787,12 +744,7 @@ fn read_hg_dirstate(repo_root: &Path) -> Result<DirstateData, io::Error> {
|
|||||||
reader.verify_checksum()?;
|
reader.verify_checksum()?;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
header => {
|
header => bail!("Unrecognized header byte: {:x}", header),
|
||||||
return Err(io::Error::new(
|
|
||||||
io::ErrorKind::InvalidInput,
|
|
||||||
format!("Unrecognized header byte: {:x}", header),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,8 +232,5 @@ fn status(opts: StatusOpts, io: &mut IO, repo: Repo) -> Result<u8> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let cwd = std::env::current_dir()?;
|
let cwd = std::env::current_dir()?;
|
||||||
match maybe_status_fastpath(repo.path(), &cwd, print_config) {
|
maybe_status_fastpath(repo.path(), &cwd, print_config, io)
|
||||||
None => Err(errors::FallbackToPython.into()),
|
|
||||||
Some(result) => result,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -127,9 +127,7 @@ Test exception logging:
|
|||||||
|
|
||||||
Note: Errors raised by the dispatch logic aren't logged here:
|
Note: Errors raised by the dispatch logic aren't logged here:
|
||||||
$ hg st --nonexistant > /dev/null
|
$ hg st --nonexistant > /dev/null
|
||||||
hg status: option --nonexistant not recognized
|
abort: option --nonexistant not recognized
|
||||||
(use 'hg status -h' to get help)
|
|
||||||
atexit handler executed
|
|
||||||
[255]
|
[255]
|
||||||
>>> import json, pprint
|
>>> import json, pprint
|
||||||
>>> with open("$LOGDIR/samplingpath.txt") as f:
|
>>> with open("$LOGDIR/samplingpath.txt") as f:
|
||||||
|
@ -11,98 +11,15 @@ Function to check that statprof ran
|
|||||||
|
|
||||||
test --profile
|
test --profile
|
||||||
|
|
||||||
$ hg st --profile 2>&1 | statprofran
|
$ hg log -r . --profile 2>&1 | statprofran
|
||||||
|
|
||||||
Abreviated version
|
Abreviated version
|
||||||
|
|
||||||
$ hg st --prof 2>&1 | statprofran
|
$ hg log -r . --prof 2>&1 | statprofran
|
||||||
|
|
||||||
In alias
|
In alias
|
||||||
|
|
||||||
$ hg --config "alias.profst=status --profile" profst 2>&1 | statprofran
|
$ hg --config "alias.proflog=log -r . --profile" proflog 2>&1 | statprofran
|
||||||
|
|
||||||
#if lsprof
|
|
||||||
|
|
||||||
$ prof='hg --config profiling.type=ls --profile'
|
|
||||||
|
|
||||||
$ $prof st 2>../out
|
|
||||||
$ grep CallCount ../out > /dev/null || cat ../out
|
|
||||||
|
|
||||||
$ $prof --config profiling.output=../out st
|
|
||||||
$ grep CallCount ../out > /dev/null || cat ../out
|
|
||||||
|
|
||||||
$ $prof --config profiling.output=blackbox --config extensions.blackbox= st
|
|
||||||
$ hg blackbox --pattern '{"profile":"_"}' | grep -o CallCount
|
|
||||||
CallCount
|
|
||||||
|
|
||||||
$ $prof --config profiling.format=text st 2>../out
|
|
||||||
$ grep CallCount ../out > /dev/null || cat ../out
|
|
||||||
|
|
||||||
$ echo "[profiling]" >> $HGRCPATH
|
|
||||||
$ echo "format=kcachegrind" >> $HGRCPATH
|
|
||||||
|
|
||||||
$ $prof st 2>../out
|
|
||||||
$ grep 'events: Ticks' ../out > /dev/null || cat ../out
|
|
||||||
|
|
||||||
$ $prof --config profiling.output=../out st
|
|
||||||
$ grep 'events: Ticks' ../out > /dev/null || cat ../out
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if lsprof serve
|
|
||||||
|
|
||||||
Profiling of HTTP requests works
|
|
||||||
|
|
||||||
$ $prof --config profiling.format=text --config profiling.output=../profile.log serve -d -p $HGPORT --pid-file ../hg.pid -A ../access.log
|
|
||||||
$ cat ../hg.pid >> $DAEMON_PIDS
|
|
||||||
$ hg -q clone -U http://localhost:$HGPORT ../clone
|
|
||||||
|
|
||||||
A single profile is logged because file logging doesn't append
|
|
||||||
$ grep CallCount ../profile.log | wc -l
|
|
||||||
\s*1 (re)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Install an extension that can sleep and guarantee a profiler has time to run
|
|
||||||
|
|
||||||
$ cat >> sleepext.py << EOF
|
|
||||||
> import time
|
|
||||||
> from edenscm.mercurial import registrar, commands
|
|
||||||
> cmdtable = {}
|
|
||||||
> command = registrar.command(cmdtable)
|
|
||||||
> @command(b'sleep', [], 'hg sleep')
|
|
||||||
> def sleep(ui, *args, **kwargs):
|
|
||||||
> time.sleep(0.1)
|
|
||||||
> EOF
|
|
||||||
|
|
||||||
$ cat >> $HGRCPATH << EOF
|
|
||||||
> [extensions]
|
|
||||||
> sleep = `pwd`/sleepext.py
|
|
||||||
> EOF
|
|
||||||
|
|
||||||
statistical profiler works
|
|
||||||
|
|
||||||
$ hg --profile sleep 2>../out
|
|
||||||
$ cat ../out | statprofran
|
|
||||||
|
|
||||||
Various statprof formatters work
|
|
||||||
|
|
||||||
$ hg --profile --config profiling.statformat=byline sleep 2>../out
|
|
||||||
$ head -n 1 ../out
|
|
||||||
% cumulative self
|
|
||||||
$ cat ../out | statprofran
|
|
||||||
|
|
||||||
$ hg --profile --config profiling.statformat=bymethod sleep 2>../out
|
|
||||||
$ head -n 1 ../out
|
|
||||||
% cumulative self
|
|
||||||
$ cat ../out | statprofran
|
|
||||||
|
|
||||||
$ hg --profile --config profiling.statformat=hotpath sleep 2>../out
|
|
||||||
$ cat ../out | statprofran
|
|
||||||
|
|
||||||
$ hg --profile --config profiling.statformat=json sleep 2>../out
|
|
||||||
$ cat ../out
|
|
||||||
\[\[-?\d+.* (re)
|
|
||||||
|
|
||||||
#if normal-layout
|
#if normal-layout
|
||||||
statprof can be used as a standalone module
|
statprof can be used as a standalone module
|
||||||
|
Loading…
Reference in New Issue
Block a user