summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVito Caputo <vcaputo@pengaru.com>2020-12-05 00:00:37 -0800
committerVito Caputo <vcaputo@pengaru.com>2020-12-05 00:29:59 -0800
commita37db3fd3ac339a0411168d22bc17e858e289c4b (patch)
tree1e83b4974e0c173837af6d4ba06f47fde61eee4b /src
parent99fa79945cdf5947bc9d12e3f506aa4cfb78cdce (diff)
report-layout: implement rudimentary `jio report layout`
This writes a .layout file for every opened journal, which describes the sequential object layout for the respective journal. Sample output: ``` Layout for "user-1000.journal" Legend: ? OBJECT_UNUSED d OBJECT_DATA f OBJECT_FIELD e OBJECT_ENTRY D OBJECT_DATA_HASH_TABLE F OBJECT_FIELD_HASH_TABLE A OBJECT_ENTRY_ARRAY T OBJECT_TAG |N| object spans N page boundaries (page size used=4096) | single page boundary +N N bytes of alignment padding + single byte alignment padding F|5344 D|448|1834896 d81+7 f50+6 d74+6 f48 d82+6 f55+ d84+4 f57+7 d80 f50+6 d122+6 f47+ d74+6 f44+4 d73+7 f44+4 d70+2 f44+4 d72 f45+3 d76+4 f44+4 d75+5 f48 d90+6 f54+2 d80 f54+2 d84+4 f55+ d123+5 f55+ d82+6 f56 d87+ f58+6 d93+3 f53+3 d|94+2 f54+2 d91+5 f59+5 d119+ f62+2 d107+5 f66+6 d105+7 f48 d108+4 f51+5 d82+6 f49+7 e480 A56 d80 d104 d74+6 d73+7 d107+5 e480 A56 A56 A56 A56 A56 A56 A56 A56 A56 A56 A56 A56 A56 A56 A56 A56 A56 A56 A56 A56 A56 d97+7 d107+5 e|480 A56 A56 A56 d136 d107+5 e480 A56 d74+6 d148+4 d107+5 e480 A88 d107+5 e480 A88 A88 A88 A56 A88 A88 A88 A88 A88 A88 A88 A88 A88 A88 A|88 A88 A88 A88 A88 A88 A88 d80 d74+6 d107+5 e480 A88 A56 d107+5 e480 A56 A56 A56 d107+5 e480 A56 d107+5 e480 A88 A56 A56 d107+5 e|480 d80 d74+6 d107+5 e480 d97+7 d107+5 e480 A232 A88 A56 A56 d142+2 d107+5 e480 A232 A232 A232 A232 A232 A|232 A232 A232 A232 A232 A232 A232 A232 A232 A232 A232 A232 A232 A232 A232 d107+5 e480 d107+5 e|480 d80 d74+6 d107+5 e480 A232 d107+5 e480 A56 A56 d107+5 e480 d107+5 e480 d107+5 e304 d80 d74+6 d107+5 e|480 d107+5 e480 A56 A56 d107+5 e480 A232 d107+5 e480 d107+5 e480 A88 d80 d74+6 d107+5 e480 A88 d107+5 e|480 A56 A56 d107+5 e480 d107+5 e480 A88 A88 ``` This provides insight into the distribution of object types, how much space is spent on alignment padding, how frequently objects land on page boundaries - something preferably avoided especially for small objects near a boundary. In the above example, we can immediately make the observation that early in the journal there are interleaved data and field objects, and this cluster of the initially added data and field objects gets torn by a page boundary. It's possible if they weren't interleaved when adding an entry and its respective data + fields, fitting all the early (common) data objects within a single page might confer some performance gain. The field objects aren't currently used by data object heavy operations, it doesn't make much sense to have them polluting the area of the initially added data objects or really any otherwise clustered data objects.
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rw-r--r--src/jio.c12
-rw-r--r--src/report-layout.c178
-rw-r--r--src/report-layout.h8
4 files changed, 198 insertions, 2 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index cfc2f3f..0ea6ebd 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -14,6 +14,8 @@ jio_SOURCES = \
readfile.h \
reclaim-tail-waste.c \
reclaim-tail-waste.h \
+ report-layout.c \
+ report-layout.h \
report-tail-waste.c \
report-tail-waste.h \
report-usage.c \
diff --git a/src/jio.c b/src/jio.c
index 2e98545..4c4fdd5 100644
--- a/src/jio.c
+++ b/src/jio.c
@@ -20,6 +20,7 @@
#include <iou.h>
#include "reclaim-tail-waste.h"
+#include "report-layout.h"
#include "report-tail-waste.h"
#include "report-usage.h"
@@ -55,6 +56,7 @@ int main(int argc, char *argv[])
" tail-waste reclaim wasted space from tails of archives\n"
"\n"
" report [subcmd] report statistics about journal files\n"
+ " layout report layout of objects, writes a .layout file per journal\n"
" usage report space used by various object types\n"
" tail-waste report extra space allocated onto tails\n"
" version print jio version\n"
@@ -98,11 +100,17 @@ int main(int argc, char *argv[])
}
} else if (!strcmp(argv[1], "report")) {
if (argc < 3) {
- printf("Usage: %s report {usage,tail-waste}\n", argv[0]);
+ printf("Usage: %s report {layout,usage,tail-waste}\n", argv[0]);
return 0;
}
- if (!strcmp(argv[2], "tail-waste")) {
+ if (!strcmp(argv[2], "layout")) {
+ r = jio_report_layout(iou, argc, argv);
+ if (r < 0) {
+ fprintf(stderr, "failed to report layout: %s\n", strerror(-r));
+ return 1;
+ }
+ } else if (!strcmp(argv[2], "tail-waste")) {
r = jio_report_tail_waste(iou, argc, argv);
if (r < 0) {
fprintf(stderr, "failed to report tail waste: %s\n", strerror(-r));
diff --git a/src/report-layout.c b/src/report-layout.c
new file mode 100644
index 0000000..e8d686e
--- /dev/null
+++ b/src/report-layout.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2020 - Vito Caputo - <vcaputo@pengaru.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <iou.h>
+#include <thunk.h>
+
+#include "bootid.h"
+#include "humane.h"
+#include "journals.h"
+#include "machid.h"
+#include "report-layout.h"
+
+#include "upstream/journal-def.h"
+
+
+static const char type_map[_OBJECT_TYPE_MAX] = {
+ [OBJECT_UNUSED] = '?',
+ [OBJECT_DATA] = 'd',
+ [OBJECT_FIELD] = 'f',
+ [OBJECT_ENTRY] = 'e',
+ [OBJECT_DATA_HASH_TABLE] = 'D',
+ [OBJECT_FIELD_HASH_TABLE] = 'F',
+ [OBJECT_ENTRY_ARRAY] = 'A',
+ [OBJECT_TAG] = 'T',
+};
+
+/* TODO: this should be either argv settable or just determined at runtime */
+#define PAGE_SIZE 4096
+
+THUNK_DEFINE_STATIC(per_data_object, uint64_t *, iter_offset, ObjectHeader *, iter_object_header, FILE *, out)
+{
+ char boundary_marker[22] = "";
+ char alignment_marker[3] = "";
+ uint64_t off, this_page, next_page, page_delta, aligned_delta;
+
+ assert(iter_offset);
+ assert(iter_object_header);
+ assert(out);
+
+ off = *iter_offset;
+
+ if (!off) {
+ fprintf(out, "\n");
+ fflush(out);
+ fclose(out);
+ return 0;
+ }
+
+
+ this_page = off & ~(PAGE_SIZE-1);
+ next_page = (off + iter_object_header->size + PAGE_SIZE-1) & ~(PAGE_SIZE-1);
+ page_delta = next_page - this_page;
+
+ if (page_delta > PAGE_SIZE * 2)
+ snprintf(boundary_marker, sizeof(boundary_marker), "|%"PRIu64"|", page_delta / PAGE_SIZE - 1);
+ else if (page_delta > PAGE_SIZE)
+ snprintf(boundary_marker, sizeof(boundary_marker), "|");
+
+ aligned_delta = ALIGN64(iter_object_header->size) - iter_object_header->size;
+
+ if (aligned_delta > 1)
+ snprintf(alignment_marker, sizeof(alignment_marker), "+%"PRIu64, aligned_delta);
+ else if (aligned_delta)
+ snprintf(alignment_marker, sizeof(alignment_marker), "+");
+
+ fprintf(out, "%s%c%s%"PRIu64"%s ",
+ this_page == off ? "| " : "",
+ type_map[iter_object_header->type],
+ boundary_marker,
+ iter_object_header->size,
+ alignment_marker);
+
+ return 0;
+}
+
+
+THUNK_DEFINE_STATIC(per_journal, iou_t *, iou, journal_t **, journal_iter)
+{
+ struct {
+ journal_t *journal;
+ Header header;
+ uint64_t iter_offset;
+ ObjectHeader iter_object_header;
+ FILE *out;
+ } *foo;
+
+ thunk_t *closure;
+ char *fname;
+ FILE *f;
+
+ assert(iou);
+ assert(journal_iter);
+
+ fname = malloc(strlen((*journal_iter)->name) + sizeof(".layout"));
+ if (!fname)
+ return -ENOMEM;
+
+ sprintf(fname, "%s.layout", (*journal_iter)->name);
+ f = fopen(fname, "w+");
+ free(fname);
+ if (!f)
+ return -errno;
+
+ fprintf(f, "Layout for \"%s\"\n", (*journal_iter)->name);
+ fprintf(f,
+ "Legend:\n"
+ "%c OBJECT_UNUSED\n"
+ "%c OBJECT_DATA\n"
+ "%c OBJECT_FIELD\n"
+ "%c OBJECT_ENTRY\n"
+ "%c OBJECT_DATA_HASH_TABLE\n"
+ "%c OBJECT_FIELD_HASH_TABLE\n"
+ "%c OBJECT_ENTRY_ARRAY\n"
+ "%c OBJECT_TAG\n\n"
+ "|N| object spans N page boundaries (page size used=%u)\n"
+ "| single page boundary\n"
+ "+N N bytes of alignment padding\n"
+ "+ single byte alignment padding\n\n",
+
+ type_map[OBJECT_UNUSED],
+ type_map[OBJECT_DATA],
+ type_map[OBJECT_FIELD],
+ type_map[OBJECT_ENTRY],
+ type_map[OBJECT_DATA_HASH_TABLE],
+ type_map[OBJECT_FIELD_HASH_TABLE],
+ type_map[OBJECT_ENTRY_ARRAY],
+ type_map[OBJECT_TAG],
+ PAGE_SIZE);
+
+ closure = THUNK_ALLOC(per_data_object, (void **)&foo, sizeof(*foo));
+ foo->journal = *journal_iter;
+ foo->out = f;
+
+ return journal_get_header(iou, &foo->journal, &foo->header, THUNK(
+ journal_iter_objects(iou, &foo->journal, &foo->header, &foo->iter_offset, &foo->iter_object_header, THUNK_INIT(
+ per_data_object(closure, &foo->iter_offset, &foo->iter_object_header, foo->out)))));
+}
+
+
+/* print the layout of contents per journal */
+int jio_report_layout(iou_t *iou, int argc, char *argv[])
+{
+ char *machid;
+ journals_t *journals;
+ journal_t *journal_iter;
+ int r;
+
+ r = machid_get(iou, &machid, THUNK(
+ journals_open(iou, &machid, O_RDONLY, &journals, THUNK(
+ journals_for_each(&journals, &journal_iter, THUNK(
+ per_journal(iou, &journal_iter)))))));
+ if (r < 0)
+ return r;
+
+ r = iou_run(iou);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
diff --git a/src/report-layout.h b/src/report-layout.h
new file mode 100644
index 0000000..bd82122
--- /dev/null
+++ b/src/report-layout.h
@@ -0,0 +1,8 @@
+#ifndef _JIO_REPORT_LAYOUT
+#define _JIO_REPORT_LAYOUT
+
+typedef struct iou_t iou_t;
+
+int jio_report_layout(iou_t *iou, int argc, char *argv[]);
+
+#endif
© All Rights Reserved