summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@edwardthomson.com>2015-01-16 18:32:48 -0500
committerEdward Thomson <ethomson@microsoft.com>2015-01-19 13:27:44 -0600
commit651afd01d6d3a536d05f8267501ef26fca9017ee (patch)
tree7d07bcd764217a5fc73721c0194f3fd4db8d284a
parent91fa31fb6f44919d5dcbaa157cfac9fb49dc44df (diff)
downloadlibgit2-651afd01d6d3a536d05f8267501ef26fca9017ee.tar.gz
checkout: introduce git_checkout_perfdata
Checkout can now provide performance data about the number of (some) syscalls performed using an optional callback. This structure remains internal-only in maintenance branches.
-rw-r--r--src/checkout.c108
-rw-r--r--src/checkout.h7
-rw-r--r--src/fileops.c55
-rw-r--r--src/fileops.h14
-rw-r--r--tests/checkout/tree.c1
5 files changed, 135 insertions, 50 deletions
diff --git a/src/checkout.c b/src/checkout.c
index 085bd6912..40a6ea0c4 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -65,6 +65,7 @@ typedef struct {
bool reload_submodules;
size_t total_steps;
size_t completed_steps;
+ git_checkout_perfdata perfdata;
} checkout_data;
typedef struct {
@@ -1218,50 +1219,86 @@ fail:
return error;
}
+static int checkout_mkdir(
+ checkout_data *data,
+ const char *path,
+ const char *base,
+ mode_t mode,
+ unsigned int flags)
+{
+ struct git_futils_mkdir_perfdata mkdir_perfdata = {0};
+
+ int error = git_futils_mkdir_withperf(
+ path, base, mode, flags, &mkdir_perfdata);
+
+ data->perfdata.mkdir_calls += mkdir_perfdata.mkdir_calls;
+ data->perfdata.stat_calls += mkdir_perfdata.stat_calls;
+ data->perfdata.chmod_calls += mkdir_perfdata.chmod_calls;
+
+ return error;
+}
+
+static int mkpath2file(
+ checkout_data *data, const char *path, unsigned int mode)
+{
+ return checkout_mkdir(
+ data, path, git_repository_workdir(data->repo), mode,
+ GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR);
+}
+
static int buffer_to_file(
+ checkout_data *data,
struct stat *st,
git_buf *buf,
const char *path,
- mode_t dir_mode,
- int file_open_flags,
mode_t file_mode)
{
int error;
- if ((error = git_futils_mkpath2file(path, dir_mode)) < 0)
+ if ((error = mkpath2file(data, path, data->opts.dir_mode)) < 0)
return error;
if ((error = git_futils_writebuffer(
- buf, path, file_open_flags, file_mode)) < 0)
+ buf, path, data->opts.file_open_flags, file_mode)) < 0)
return error;
- if (st != NULL && (error = p_stat(path, st)) < 0)
- giterr_set(GITERR_OS, "Error statting '%s'", path);
+ if (st) {
+ data->perfdata.stat_calls++;
+
+ if ((error = p_stat(path, st)) < 0) {
+ giterr_set(GITERR_OS, "Error statting '%s'", path);
+ return error;
+ }
+ }
- else if (GIT_PERMS_IS_EXEC(file_mode) &&
- (error = p_chmod(path, file_mode)) < 0)
- giterr_set(GITERR_OS, "Failed to set permissions on '%s'", path);
+ if (GIT_PERMS_IS_EXEC(file_mode)) {
+ data->perfdata.chmod_calls++;
+
+ if ((error = p_chmod(path, file_mode)) < 0)
+ giterr_set(GITERR_OS, "Failed to set permissions on '%s'", path);
+ }
return error;
}
static int blob_content_to_file(
+ checkout_data *data,
struct stat *st,
git_blob *blob,
const char *path,
const char * hint_path,
- mode_t entry_filemode,
- git_checkout_options *opts)
+ mode_t entry_filemode)
{
- int error = 0;
- mode_t file_mode = opts->file_mode ? opts->file_mode : entry_filemode;
+ mode_t file_mode = data->opts.file_mode ?
+ data->opts.file_mode : entry_filemode;
git_buf out = GIT_BUF_INIT;
git_filter_list *fl = NULL;
+ int error = 0;
if (hint_path == NULL)
hint_path = path;
- if (!opts->disable_filters)
+ if (!data->opts.disable_filters)
error = git_filter_list_load(
&fl, git_blob_owner(blob), blob, hint_path,
GIT_FILTER_TO_WORKTREE, GIT_FILTER_OPT_DEFAULT);
@@ -1272,9 +1309,7 @@ static int blob_content_to_file(
git_filter_list_free(fl);
if (!error) {
- error = buffer_to_file(
- st, &out, path, opts->dir_mode, opts->file_open_flags, file_mode);
-
+ error = buffer_to_file(data, st, &out, path, file_mode);
st->st_mode = entry_filemode;
git_buf_free(&out);
@@ -1284,22 +1319,21 @@ static int blob_content_to_file(
}
static int blob_content_to_link(
+ checkout_data *data,
struct stat *st,
git_blob *blob,
- const char *path,
- mode_t dir_mode,
- int can_symlink)
+ const char *path)
{
git_buf linktarget = GIT_BUF_INIT;
int error;
- if ((error = git_futils_mkpath2file(path, dir_mode)) < 0)
+ if ((error = mkpath2file(data, path, data->opts.dir_mode)) < 0)
return error;
if ((error = git_blob__getbuf(&linktarget, blob)) < 0)
return error;
- if (can_symlink) {
+ if (data->can_symlink) {
if ((error = p_symlink(git_buf_cstr(&linktarget), path)) < 0)
giterr_set(GITERR_OS, "Could not create symlink %s\n", path);
} else {
@@ -1307,6 +1341,8 @@ static int blob_content_to_link(
}
if (!error) {
+ data->perfdata.stat_calls++;
+
if ((error = p_lstat(path, st)) < 0)
giterr_set(GITERR_CHECKOUT, "Could not stat symlink %s", path);
@@ -1350,6 +1386,7 @@ static int checkout_submodule_update_index(
if (git_buf_puts(&data->path, file->path) < 0)
return -1;
+ data->perfdata.stat_calls++;
if (p_stat(git_buf_cstr(&data->path), &st) < 0) {
giterr_set(
GITERR_CHECKOUT, "Could not stat submodule %s\n", file->path);
@@ -1371,7 +1408,8 @@ static int checkout_submodule(
if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0)
return 0;
- if ((error = git_futils_mkdir(
+ if ((error = checkout_mkdir(
+ data,
file->path, data->opts.target_directory,
data->opts.dir_mode, GIT_MKDIR_PATH)) < 0)
return error;
@@ -1410,10 +1448,13 @@ static void report_progress(
data->opts.progress_payload);
}
-static int checkout_safe_for_update_only(const char *path, mode_t expected_mode)
+static int checkout_safe_for_update_only(
+ checkout_data *data, const char *path, mode_t expected_mode)
{
struct stat st;
+ data->perfdata.stat_calls++;
+
if (p_lstat(path, &st) < 0) {
/* if doesn't exist, then no error and no update */
if (errno == ENOENT || errno == ENOTDIR)
@@ -1446,11 +1487,9 @@ static int checkout_write_content(
return error;
if (S_ISLNK(mode))
- error = blob_content_to_link(
- st, blob, full_path, data->opts.dir_mode, data->can_symlink);
+ error = blob_content_to_link(data, st, blob, full_path);
else
- error = blob_content_to_file(
- st, blob, full_path, hint_path, mode, &data->opts);
+ error = blob_content_to_file(data, st, blob, full_path, hint_path, mode);
git_blob_free(blob);
@@ -1481,7 +1520,7 @@ static int checkout_blob(
if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) {
int rval = checkout_safe_for_update_only(
- git_buf_cstr(&data->path), file->mode);
+ data, git_buf_cstr(&data->path), file->mode);
if (rval <= 0)
return rval;
}
@@ -1736,7 +1775,7 @@ static int checkout_write_entry(
}
if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0 &&
- (error = checkout_safe_for_update_only(git_buf_cstr(&data->path), side->mode)) <= 0)
+ (error = checkout_safe_for_update_only(data, git_buf_cstr(&data->path), side->mode)) <= 0)
return error;
return checkout_write_content(data,
@@ -1835,7 +1874,7 @@ static int checkout_write_merge(
goto done;
if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0 &&
- (error = checkout_safe_for_update_only(git_buf_cstr(&path_workdir), result.mode)) <= 0)
+ (error = checkout_safe_for_update_only(data, git_buf_cstr(&path_workdir), result.mode)) <= 0)
goto done;
if (!data->opts.disable_filters) {
@@ -1851,7 +1890,7 @@ static int checkout_write_merge(
out_data.size = result.len;
}
- if ((error = git_futils_mkpath2file(path_workdir.ptr, 0755)) < 0 ||
+ if ((error = mkpath2file(data, path_workdir.ptr, data->opts.dir_mode)) < 0 ||
(error = git_filebuf_open(&output, git_buf_cstr(&path_workdir), GIT_FILEBUF_DO_NOT_BUFFER, result.mode)) < 0 ||
(error = git_filebuf_write(&output, out_data.ptr, out_data.size)) < 0 ||
(error = git_filebuf_commit(&output)) < 0)
@@ -2056,8 +2095,9 @@ static int checkout_data_init(
if (!data->opts.target_directory)
data->opts.target_directory = git_repository_workdir(repo);
else if (!git_path_isdir(data->opts.target_directory) &&
- (error = git_futils_mkdir(data->opts.target_directory, NULL,
- GIT_DIR_MODE, GIT_MKDIR_VERIFY_DIR)) < 0)
+ (error = checkout_mkdir(data,
+ data->opts.target_directory, NULL,
+ GIT_DIR_MODE, GIT_MKDIR_VERIFY_DIR)) < 0)
goto cleanup;
/* refresh config and index content unless NO_REFRESH is given */
diff --git a/src/checkout.h b/src/checkout.h
index f1fe69628..6f21e6cc8 100644
--- a/src/checkout.h
+++ b/src/checkout.h
@@ -12,6 +12,13 @@
#define GIT_CHECKOUT__NOTIFY_CONFLICT_TREE (1u << 12)
+/** Internal structure; this is exposed in future versions. */
+typedef struct {
+ size_t mkdir_calls;
+ size_t stat_calls;
+ size_t chmod_calls;
+} git_checkout_perfdata;
+
/**
* Update the working directory to match the target iterator. The
* expected baseline value can be passed in via the checkout options
diff --git a/src/fileops.c b/src/fileops.c
index bd9d27c7a..926813f5e 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -279,11 +279,12 @@ void git_futils_mmap_free(git_map *out)
p_munmap(out);
}
-int git_futils_mkdir(
+int git_futils_mkdir_withperf(
const char *path,
const char *base,
mode_t mode,
- uint32_t flags)
+ uint32_t flags,
+ struct git_futils_mkdir_perfdata *perfdata)
{
int error = -1;
git_buf make_path = GIT_BUF_INIT;
@@ -352,15 +353,20 @@ int git_futils_mkdir(
st.st_mode = 0;
/* make directory */
+ perfdata->mkdir_calls++;
+
if (p_mkdir(make_path.ptr, mode) < 0) {
int tmp_errno = giterr_system_last();
/* ignore error if not at end or if directory already exists */
- if (lastch == '\0' &&
- (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode))) {
- giterr_system_set(tmp_errno);
- giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr);
- goto done;
+ if (lastch == '\0') {
+ perfdata->stat_calls++;
+
+ if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) {
+ giterr_system_set(tmp_errno);
+ giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr);
+ goto done;
+ }
}
/* with exclusive create, existing dir is an error */
@@ -374,11 +380,15 @@ int git_futils_mkdir(
/* chmod if requested and necessary */
if (((flags & GIT_MKDIR_CHMOD_PATH) != 0 ||
(lastch == '\0' && (flags & GIT_MKDIR_CHMOD) != 0)) &&
- st.st_mode != mode &&
- (error = p_chmod(make_path.ptr, mode)) < 0 &&
- lastch == '\0') {
- giterr_set(GITERR_OS, "Failed to set permissions on '%s'", make_path.ptr);
- goto done;
+ st.st_mode != mode) {
+
+ perfdata->chmod_calls++;
+
+ if ((error = p_chmod(make_path.ptr, mode)) < 0 &&
+ lastch == '\0') {
+ giterr_set(GITERR_OS, "Failed to set permissions on '%s'", make_path.ptr);
+ goto done;
+ }
}
}
@@ -386,10 +396,13 @@ int git_futils_mkdir(
/* check that full path really is a directory if requested & needed */
if ((flags & GIT_MKDIR_VERIFY_DIR) != 0 &&
- lastch != '\0' &&
- (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode))) {
- giterr_set(GITERR_OS, "Path is not a directory '%s'", make_path.ptr);
- error = GIT_ENOTFOUND;
+ lastch != '\0') {
+ perfdata->stat_calls++;
+
+ if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) {
+ giterr_set(GITERR_OS, "Path is not a directory '%s'", make_path.ptr);
+ error = GIT_ENOTFOUND;
+ }
}
done:
@@ -397,6 +410,16 @@ done:
return error;
}
+int git_futils_mkdir(
+ const char *path,
+ const char *base,
+ mode_t mode,
+ uint32_t flags)
+{
+ struct git_futils_mkdir_perfdata perfdata = {0};
+ return git_futils_mkdir_withperf(path, base, mode, flags, &perfdata);
+}
+
int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
{
return git_futils_mkdir(path, base, mode, GIT_MKDIR_PATH);
diff --git a/src/fileops.h b/src/fileops.h
index 4f5700a99..65b59522c 100644
--- a/src/fileops.h
+++ b/src/fileops.h
@@ -84,6 +84,13 @@ typedef enum {
GIT_MKDIR_VERIFY_DIR = 64,
} git_futils_mkdir_flags;
+struct git_futils_mkdir_perfdata
+{
+ size_t stat_calls;
+ size_t mkdir_calls;
+ size_t chmod_calls;
+};
+
/**
* Create a directory or entire path.
*
@@ -95,8 +102,15 @@ typedef enum {
* @param base Root for relative path. These directories will never be made.
* @param mode The mode to use for created directories.
* @param flags Combination of the mkdir flags above.
+ * @param perfdata Performance data, use `git_futils_mkdir` if you don't want this data.
* @return 0 on success, else error code
*/
+extern int git_futils_mkdir_withperf(const char *path, const char *base, mode_t mode, uint32_t flags, struct git_futils_mkdir_perfdata *perfdata);
+
+/**
+ * Create a directory or entire path. Similar to `git_futils_mkdir_withperf`
+ * without performance data.
+ */
extern int git_futils_mkdir(const char *path, const char *base, mode_t mode, uint32_t flags);
/**
diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c
index 0933f64f7..bff6245e3 100644
--- a/tests/checkout/tree.c
+++ b/tests/checkout/tree.c
@@ -948,3 +948,4 @@ void test_checkout_tree__filemode_preserved_in_index(void)
git_commit_free(commit);
git_index_free(index);
}
+