#include "istream.h"
#include "istream-seekable.h"
#include "ostream.h"
+#include "timing.h"
+#include "time-util.h"
+#include "istream-fs-stats.h"
#include "fs-api-private.h"
struct fs_api_module_register fs_api_module_register = { 0 };
{
const struct fs *fs_class;
const char *temp_file_prefix;
+ unsigned int i;
fs_class = fs_class_find(driver);
if (fs_class == NULL) {
if (fs_alloc(fs_class, args, set, fs_r, error_r) < 0)
return -1;
+ for (i = 0; i < FS_OP_COUNT; i++)
+ (*fs_r)->stats.timings[i] = timing_init();
+
temp_file_prefix = set->temp_file_prefix != NULL ?
set->temp_file_prefix : ".temp.dovecot";
(*fs_r)->temp_path_prefix = i_strconcat(set->temp_dir, "/",
struct fs *fs = *_fs;
string_t *last_error = fs->last_error;
struct array module_contexts_arr = fs->module_contexts.arr;
+ unsigned int i;
i_assert(fs->refcount > 0);
T_BEGIN {
fs->v.deinit(fs);
} T_END;
+ for (i = 0; i < FS_OP_COUNT; i++)
+ timing_deinit(&fs->stats.timings[i]);
array_free_i(&module_contexts_arr);
str_free(&last_error);
}
} T_END;
}
+static void fs_file_timing_start(struct fs_file *file, enum fs_op op)
+{
+ if (!file->fs->set.enable_timing)
+ return;
+ if (file->timing_start[op].tv_sec == 0) {
+ if (gettimeofday(&file->timing_start[op], NULL) < 0)
+ i_fatal("gettimeofday() failed: %m");
+ }
+}
+
+static void
+fs_timing_end(struct timing *timing, const struct timeval *start_tv)
+{
+ struct timeval now;
+ long long diff;
+
+ if (gettimeofday(&now, NULL) < 0)
+ i_fatal("gettimeofday() failed: %m");
+
+ diff = timeval_diff_usecs(&now, start_tv);
+ if (diff > 0)
+ timing_add_usecs(timing, diff);
+}
+
+void fs_file_timing_end(struct fs_file *file, enum fs_op op)
+{
+ if (!file->fs->set.enable_timing || file->timing_start[op].tv_sec == 0)
+ return;
+
+ fs_timing_end(file->fs->stats.timings[op], &file->timing_start[op]);
+ /* don't count this again */
+ file->timing_start[op].tv_sec = 0;
+}
+
int fs_get_metadata(struct fs_file *file,
const ARRAY_TYPE(fs_metadata) **metadata_r)
{
!file->lookup_metadata_counted) {
file->lookup_metadata_counted = TRUE;
file->fs->stats.lookup_metadata_count++;
+ fs_file_timing_start(file, FS_OP_METADATA);
}
T_BEGIN {
ret = file->fs->v.get_metadata(file, metadata_r);
} T_END;
+ if (!(ret < 0 && errno == EAGAIN))
+ fs_file_timing_end(file, FS_OP_METADATA);
return ret;
}
if (!file->read_or_prefetch_counted) {
file->read_or_prefetch_counted = TRUE;
file->fs->stats.prefetch_count++;
+ fs_file_timing_start(file, FS_OP_PREFETCH);
}
T_BEGIN {
ret = file->fs->v.prefetch(file, length);
} T_END;
+ fs_file_timing_end(file, FS_OP_PREFETCH);
return ret;
}
if (!file->read_or_prefetch_counted) {
file->read_or_prefetch_counted = TRUE;
file->fs->stats.read_count++;
+ fs_file_timing_start(file, FS_OP_READ);
}
if (file->fs->v.read != NULL) {
T_BEGIN {
ret = file->fs->v.read(file, buf, size);
} T_END;
+ if (!(ret < 0 && errno == EAGAIN))
+ fs_file_timing_end(file, FS_OP_READ);
return ret;
}
if (!file->read_or_prefetch_counted) {
file->read_or_prefetch_counted = TRUE;
file->fs->stats.read_count++;
+ fs_file_timing_start(file, FS_OP_READ);
}
if (file->seekable_input != NULL) {
} T_END;
if (input->stream_errno != 0) {
/* read failed already */
+ fs_file_timing_end(file, FS_OP_READ);
return input;
}
+ if (file->fs->set.enable_timing) {
+ struct istream *input2 = i_stream_create_fs_stats(input, file);
+
+ i_stream_unref(&input);
+ input = input2;
+ }
if ((file->flags & FS_OPEN_FLAG_SEEKABLE) != 0)
want_seekable = TRUE;
int ret;
if (file->fs->v.write != NULL) {
+ fs_file_timing_start(file, FS_OP_WRITE);
T_BEGIN {
ret = file->fs->v.write(file, data, size);
} T_END;
- if (!(ret < 0 && errno == EAGAIN))
+ if (!(ret < 0 && errno == EAGAIN)) {
file->fs->stats.write_count++;
+ fs_file_timing_end(file, FS_OP_WRITE);
+ }
return ret;
}
{
int ret;
+ fs_file_timing_start(file, FS_OP_WRITE);
T_BEGIN {
ret = file->fs->v.write_stream_finish(file, success);
} T_END;
if (ret != 0) {
+ fs_file_timing_end(file, FS_OP_WRITE);
file->metadata_changed = FALSE;
} else {
/* write didn't finish yet. this shouldn't happen if we
return errno == ENOENT ? 0 : -1;
}
file->fs->stats.exists_count++;
+ fs_file_timing_start(file, FS_OP_EXISTS);
T_BEGIN {
ret = file->fs->v.exists(file);
} T_END;
+ if (!(ret < 0 && errno == EAGAIN))
+ fs_file_timing_end(file, FS_OP_EXISTS);
return ret;
}
!file->lookup_metadata_counted && !file->stat_counted) {
file->stat_counted = TRUE;
file->fs->stats.stat_count++;
+ fs_file_timing_start(file, FS_OP_STAT);
}
T_BEGIN {
ret = file->fs->v.stat(file, st_r);
} T_END;
+ if (!(ret < 0 && errno == EAGAIN))
+ fs_file_timing_end(file, FS_OP_STAT);
return ret;
}
i_assert(src->fs == dest->fs);
dest->fs->stats.copy_count++;
+ fs_file_timing_start(dest, FS_OP_COPY);
T_BEGIN {
ret = src->fs->v.copy(src, dest);
} T_END;
- if (!(ret < 0 && errno == EAGAIN))
+ if (!(ret < 0 && errno == EAGAIN)) {
+ fs_file_timing_end(dest, FS_OP_COPY);
dest->metadata_changed = FALSE;
+ }
return ret;
}
T_BEGIN {
ret = dest->fs->v.copy(NULL, dest);
} T_END;
- if (!(ret < 0 && errno == EAGAIN))
+ if (!(ret < 0 && errno == EAGAIN)) {
+ fs_file_timing_end(dest, FS_OP_COPY);
dest->metadata_changed = FALSE;
+ }
return ret;
}
i_assert(src->fs == dest->fs);
dest->fs->stats.rename_count++;
+ fs_file_timing_start(dest, FS_OP_RENAME);
T_BEGIN {
ret = src->fs->v.rename(src, dest);
} T_END;
+ if (!(ret < 0 && errno == EAGAIN))
+ fs_file_timing_end(dest, FS_OP_RENAME);
return ret;
}
int ret;
file->fs->stats.delete_count++;
+ fs_file_timing_start(file, FS_OP_DELETE);
T_BEGIN {
ret = file->fs->v.delete_file(file);
} T_END;
+ if (!(ret < 0 && errno == EAGAIN))
+ fs_file_timing_end(file, FS_OP_DELETE);
return ret;
}
fs_iter_init(struct fs *fs, const char *path, enum fs_iter_flags flags)
{
struct fs_iter *iter;
+ struct timeval now = ioloop_timeval;
i_assert((flags & FS_ITER_FLAG_OBJECTIDS) == 0 ||
(fs_get_properties(fs) & FS_PROPERTY_OBJECTIDS) != 0);
fs->stats.iter_count++;
+ if (fs->set.enable_timing) {
+ if (gettimeofday(&now, NULL) < 0)
+ i_fatal("gettimeofday() failed: %m");
+ }
T_BEGIN {
iter = fs->v.iter_init(fs, path, flags);
} T_END;
+ iter->start_time = now;
DLLIST_PREPEND(&fs->iters, iter);
return iter;
}
T_BEGIN {
ret = iter->fs->v.iter_next(iter);
} T_END;
+ if (iter->start_time.tv_sec != 0 &&
+ (ret != NULL || !fs_iter_have_more(iter))) {
+ /* first result returned - count this as the finish time, since
+ we don't want to count the time caller spends on this
+ iteration. */
+ fs_timing_end(iter->fs->stats.timings[FS_OP_ITER], &iter->start_time);
+ /* don't count this again */
+ iter->start_time.tv_sec = 0;
+ }
return ret;
}