+/**
+ * @brief format a log header
+ *
+ * Usage: s = format (0, "%U", format_maplog_header, headerp, verbose);
+ * @param [in] h clib_maplog_header_t pointer
+ * @param [in] verbose self-explanatory
+ */
+u8 *
+format_maplog_header (u8 * s, va_list * args)
+{
+ clib_maplog_header_t *h = va_arg (*args, clib_maplog_header_t *);
+ int verbose = va_arg (*args, int);
+
+ if (!verbose)
+ goto brief;
+ s = format (s, "basename %s ", h->file_basename);
+ s = format (s, "log ver %d.%d.%d app id %u ver %d.%d.%d %s %s\n",
+ h->maplog_major_version,
+ h->maplog_minor_version,
+ h->maplog_patch_version,
+ h->application_id,
+ h->application_major_version,
+ h->application_minor_version, h->application_patch_version,
+ h->maplog_flag_circular ? "circular" : "linear",
+ h->maplog_flag_wrapped ? "wrapped" : "not wrapped");
+ s = format (s, " records are %d %d-byte cachelines\n",
+ h->record_size_in_cachelines, h->cacheline_size);
+ s = format (s, " files are %lld records long, %lld files\n",
+ h->file_size_in_records, h->number_of_files);
+ s = format (s, " %lld records total\n", h->number_of_records);
+ return s;
+
+brief:
+ s = format (s, "%s %lld records %lld files %lld records/file",
+ h->file_basename, h->number_of_records, h->number_of_files,
+ h->file_size_in_records);
+ return s;
+}
+
+/**
+ * @brief Process a complete maplog
+ *
+ * Reads the maplog header. Map and process all log segments in order.
+ * Calls the callback function once per file with a record count.
+ *
+ * Note: if the file header isn't updated by calling
+ * clib_maplog_close(), it will appear to have an infinite
+ * number of records in an infinite number of files.
+ *
+ * So long as the callback function understands that possibility
+ * - by simply ignoring NULL records - the scheme still
+ * works...
+ *
+ * @param [in] file_basename Same basename supplied to clib_maplog_init
+ * @param [in] fp_arg Callback function pointer
+ */
+int
+clib_maplog_process (char *file_basename, void *fp_arg)
+{
+ clib_maplog_header_t _h, *h = &_h;
+ int fd, rv = 0;
+ u64 file_index;
+ u64 file_size_in_bytes;
+ u8 *header_filename, *this_filename = 0;
+ u8 *file_baseva;
+ int (*fp) (clib_maplog_header_t *, void *data, u64 count);
+ u64 records_this_file, records_left;
+ ASSERT (fp_arg);
+
+ fp = fp_arg;
+
+ header_filename = format (0, "%s_header%c", file_basename, 0);
+
+ fd = open ((char *) header_filename, O_RDONLY, 0600);
+ if (fd < 0)
+ {
+ clib_unix_warning ("open maplog header");
+ rv = -1;
+ goto out;
+ }
+ rv = read (fd, h, sizeof (*h));
+ if (rv != sizeof (*h))
+ {
+ clib_unix_warning ("read maplog header");
+ rv = -2;
+ goto out;
+ }
+ (void) close (fd);
+ fd = -1;
+
+ file_size_in_bytes = h->file_size_in_records
+ * h->record_size_in_cachelines * CLIB_CACHE_LINE_BYTES;
+
+ records_left = h->number_of_records;
+
+ for (file_index = 0; file_index < h->number_of_files; file_index++)
+ {
+ vec_reset_length (this_filename);
+ this_filename = format (this_filename, "%s_%llu%c", file_basename,
+ file_index, 0);
+ fd = open ((char *) this_filename, O_RDONLY, 0600);
+ if (fd < 0)
+ {
+ rv = -3;
+ goto out;
+ }
+
+ file_baseva =
+ mmap (0, file_size_in_bytes, PROT_READ, MAP_SHARED, fd, 0);
+ (void) close (fd);
+ fd = -1;
+ if (file_baseva == (u8 *) MAP_FAILED)
+ {
+ clib_unix_warning ("mmap");
+ rv = -4;
+ goto out;
+ }
+
+ records_this_file = (records_left > h->file_size_in_records) ?
+ h->file_size_in_records : records_left;
+
+ /*
+ * Normal log, or a circular non-wrapped log, or a circular
+ * wrapped log which happens to be exactly linear
+ */
+ if (h->maplog_flag_circular == 0 || h->maplog_flag_wrapped == 0 ||
+ ((h->number_of_records % h->file_size_in_records) == 0))
+ (*fp) (h, file_baseva, records_this_file);
+ else
+ {
+ /* "Normal" wrapped circular log */
+ u64 first_chunk_record_index = h->number_of_records &
+ (h->file_size_in_records - 1);
+ u64 first_chunk_number_of_records = records_this_file -
+ first_chunk_record_index;
+ u8 *chunk_baseva = file_baseva +
+ first_chunk_record_index * h->record_size_in_cachelines *
+ h->cacheline_size;
+ (*fp) (h, chunk_baseva, first_chunk_number_of_records);
+ (*fp) (h, file_baseva,
+ records_this_file - first_chunk_number_of_records);
+ }
+
+ if (munmap (file_baseva, file_size_in_bytes) < 0)
+ {
+ clib_warning ("munmap");
+ rv = -5;
+ /* but don't stop... */
+ }
+ records_left -= records_this_file;
+ if (records_left == 0)
+ break;
+ }
+
+out:
+ if (fd >= 0)
+ (void) close (fd);
+
+ vec_free (this_filename);
+ vec_free (header_filename);
+ return rv;
+}
+
+