summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--bcachefs.c5
-rw-r--r--cmd_debug.c79
-rw-r--r--cmds.h2
-rw-r--r--tools-util.h13
5 files changed, 95 insertions, 5 deletions
diff --git a/.gitignore b/.gitignore
index 7e4ee7a..6692ae8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@ bcachefs
tags
cscope*
bcachefs-tools
+fragmentation-analyze
diff --git a/bcachefs.c b/bcachefs.c
index 1c56ead..e8a6d0b 100644
--- a/bcachefs.c
+++ b/bcachefs.c
@@ -70,7 +70,8 @@ static void usage(void)
"Debug:\n"
"These commands work on offline, unmounted filesystems\n"
" dump Dump filesystem metadata to a qcow2 image\n"
- " list List filesystem metadata in textual form\n");
+ " list List filesystem metadata in textual form\n"
+ " frag-analyze Analyze file fragmentation\n");
}
static char *full_cmd;
@@ -188,6 +189,8 @@ int main(int argc, char *argv[])
return cmd_dump(argc, argv);
if (!strcmp(cmd, "list"))
return cmd_list(argc, argv);
+ if (!strcmp(cmd, "frag-analyze"))
+ return cmd_frag_analyze(argc, argv);
printf("Unknown command %s\n", cmd);
usage();
diff --git a/cmd_debug.c b/cmd_debug.c
index 11d73b3..7523b84 100644
--- a/cmd_debug.c
+++ b/cmd_debug.c
@@ -330,3 +330,82 @@ int cmd_list(int argc, char *argv[])
bch2_fs_stop(c);
return 0;
}
+
+int cmd_frag_analyze(int argc, char *argv[])
+{
+ ranges extents = { NULL };
+ char *line = NULL;
+ size_t len = 0;
+ bool sort = false;
+ int opt;
+
+ while ((opt = getopt(argc, argv, "s")) != -1)
+ switch (opt) {
+ case 's':
+ sort = true;
+ break;
+ }
+
+ while (getline(&line, &len, stdin) != -1) {
+ char *p = strchr(line, '\n');
+ if (p)
+ *p = 0;
+
+ int fd = open(line, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "cannot open %s: %m\n", line);
+ continue;
+ }
+
+ struct fiemap_iter iter;
+ struct fiemap_extent e;
+
+ fiemap_for_each(fd, iter, e) {
+ if (e.fe_flags & (FIEMAP_EXTENT_UNKNOWN|
+ FIEMAP_EXTENT_DATA_INLINE))
+ continue;
+
+ range_add(&extents,
+ e.fe_physical >> 10,
+ e.fe_length >> 10);
+ }
+
+ close(fd);
+ }
+
+ if (sort)
+ ranges_sort_merge(&extents);
+
+ printf(" start end gap\n");
+ unsigned nr_gaps = 0;
+ u64 gap_total = 0;
+ u64 data_total = 0;
+
+ struct range *i;
+ darray_foreach(i, extents) {
+ s64 gap = 0;
+
+ if (i + 1 < extents.item + extents.size)
+ gap = i[1].start > i[0].start
+ ? i[1].start - i[0].end
+ : i[0].start - i[1].end;
+
+ if (gap < 0)
+ gap = -gap;
+
+ nr_gaps += gap != 0;
+ gap_total += gap;
+ data_total += i->end - i->start;
+
+ printf("%16llu %15llu %15llu\n", i->start, i->end, gap);
+ }
+
+ darray_free(extents);
+
+ printf("nr gaps %u avg gap %llu avg data %llu\n",
+ nr_gaps,
+ nr_gaps ? gap_total / nr_gaps : 0,
+ data_total / (nr_gaps + 1));
+
+ return 0;
+}
diff --git a/cmds.h b/cmds.h
index 258a823..d495f27 100644
--- a/cmds.h
+++ b/cmds.h
@@ -43,4 +43,6 @@ int cmd_list(int argc, char *argv[]);
int cmd_migrate(int argc, char *argv[]);
int cmd_migrate_superblock(int argc, char *argv[]);
+int cmd_frag_analyze(int argc, char *argv[]);
+
#endif /* _CMDS_H */
diff --git a/tools-util.h b/tools-util.h
index 57f61e5..c5f2c55 100644
--- a/tools-util.h
+++ b/tools-util.h
@@ -86,10 +86,15 @@ typedef darray(struct range) ranges;
static inline void range_add(ranges *data, u64 offset, u64 size)
{
- darray_append(*data, (struct range) {
- .start = offset,
- .end = offset + size
- });
+ if (data->size &&
+ data->item[data->size - 1].end == offset) {
+ data->item[data->size - 1].end += size;
+ } else {
+ darray_append(*data, (struct range) {
+ .start = offset,
+ .end = offset + size
+ });
+ }
}
void ranges_sort_merge(ranges *);