#include "mds_client.h"
#include "metric.h"
-/* Export the g_registrations array for debugfs code */
-extern struct ceph_san_log_registration g_registrations[];
-
static int mdsmap_show(struct seq_file *s, void *p)
{
int i;
return 0;
}
-/* Simple wrapper to format jiffies to a human-readable timestamp */
-int jiffies_to_formatted_time(unsigned long jiffies_value, char *buffer, size_t buffer_len)
+/* @buffer: The buffer to store the formatted date and time string.
+ * @buffer_len: The length of the buffer.
+ *
+ * Returns: The number of characters written to the buffer, or a negative error code.
+ */
+static int jiffies_to_formatted_time(unsigned long jiffies_value, char *buffer, size_t buffer_len)
{
- struct timespec64 ts;
+ unsigned long seconds;
struct tm tm_time;
- jiffies_to_timespec64(jiffies_value, &ts);
- time64_to_tm(ts.tv_sec, 0, &tm_time);
+ // Convert jiffies to seconds
+ seconds = jiffies_value / HZ;
+
+ // Manually convert seconds to struct tm
+ // This is a simplified conversion without timezone handling
+ tm_time.tm_sec = seconds % 60;
+ seconds /= 60;
+ tm_time.tm_min = seconds % 60;
+ seconds /= 60;
+ tm_time.tm_hour = seconds % 24;
+ seconds /= 24;
+ // Simplified date calculation (assuming Unix epoch start at 1970-01-01)
+ tm_time.tm_mday = (seconds % 365) + 1; // Simplified day of month
+ tm_time.tm_mon = (seconds / 365) % 12; // Simplified month
+ tm_time.tm_year = (seconds / 365 / 12) + 70; // Simplified year since 1970
+
+ // Format the time into the buffer
return snprintf(buffer, buffer_len, "%04ld-%02d-%02d %02d:%02d:%02d",
tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday,
tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec);
static int ceph_san_tls_show(struct seq_file *s, void *p)
{
- struct ceph_san_tls_ctx *ctx;
- struct ceph_san_log_iter iter;
- struct ceph_san_log_entry *entry;
- struct ceph_san_log_registration *reg;
- char time_str[32];
- char cmdline[TASK_COMM_LEN];
- int count = 0;
-
- ctx = ceph_san_get_tls_ctx();
- if (!ctx)
- return -ENOENT;
-
- get_task_comm(cmdline, current);
-
- seq_printf(s, "Thread: %s [%d]\n", cmdline, task_pid_nr(current));
- seq_printf(s, "---------------------------------------------------------\n");
-
- ceph_san_log_iter_init(&iter, &ctx->pf);
-
- while ((entry = ceph_san_log_iter_next(&iter)) != NULL) {
- if (entry->debug_poison != CEPH_SAN_LOG_ENTRY_POISON) {
- seq_printf(s, "ERROR: Corrupted log entry at offset %llu\n", iter.prev_offset);
- continue;
- }
-
- /* Get registration for this entry */
- if (entry->reg_id >= CEPH_SAN_LOG_MAX_REGISTRATIONS) {
- seq_printf(s, "ERROR: Invalid registration ID %u\n", entry->reg_id);
- continue;
- }
-
- reg = &g_registrations[entry->reg_id];
-
- /* Format timestamp */
- jiffies_to_formatted_time(entry->ts, time_str, sizeof(time_str));
-
- /* Output the log entry with all registration info */
- seq_printf(s, "[%s] %s:%s:%u - %s\n",
- time_str,
- reg->file, reg->func, reg->line,
- entry->buffer);
- count++;
- }
-
- if (count == 0)
- seq_printf(s, "No log entries found\n");
- else
- seq_printf(s, "----- End of log: %d entries -----\n", count);
-
- return 0;
+ struct ceph_san_tls_ctx *ctx;
+ struct ceph_san_log_iter iter;
+ struct ceph_san_log_entry *entry;
+
+ /* Lock the logger to safely traverse the contexts list */
+ spin_lock(&g_logger.lock);
+
+ list_for_each_entry(ctx, &g_logger.contexts, list) {
+
+ /* Initialize iterator for this context's pagefrag */
+ ceph_san_log_iter_init(&iter, &ctx->pf);
+ int pid = ctx->pid;
+ char *comm = ctx->comm;
+
+ /* Lock the pagefrag before accessing entries */
+ spin_lock_bh(&ctx->pf.lock);
+
+ /* Iterate through all log entries in this context */
+ while ((entry = ceph_san_log_iter_next(&iter)) != NULL) {
+ if (entry->debug_poison != CEPH_SAN_LOG_ENTRY_POISON) {
+ seq_puts(s, " [Corrupted Entry]\n");
+ continue;
+ }
+
+ char datetime_str[32];
+ jiffies_to_formatted_time(entry->ts, datetime_str, sizeof(datetime_str));
+
+ seq_printf(s, "[%d][%s][%s]:%s:%s:%u: %.*s",
+ pid,
+ comm,
+ datetime_str,
+ entry->file,
+ entry->func,
+ entry->line,
+ entry->len,
+ entry->buffer);
+ }
+
+ /* Unlock the pagefrag after we're done with this context */
+ spin_unlock_bh(&ctx->pf.lock);
+ }
+
+ spin_unlock(&g_logger.lock);
+ return 0;
}
static int print_task_cgroup(struct task_struct *task, char *cgroup_path, size_t size)
#include <linux/spinlock.h>
#include <linux/ceph/ceph_san_batch.h>
#include <linux/ceph/ceph_san_pagefrag.h>
-#include <linux/stdarg.h>
/* Maximum length of a log entry buffer */
#define CEPH_SAN_LOG_MAX_LEN 256
#define CEPH_SAN_LOG_ENTRY_POISON 0xDEADBEEF
-#define CEPH_SAN_LOG_MAX_REGISTRATIONS 1024
-
-/* Helper function to format jiffies into human readable time */
-int jiffies_to_formatted_time(unsigned long jiffies_value, char *buffer, size_t buffer_len);
-
-/* Get TLS context for current task */
-struct ceph_san_tls_ctx *ceph_san_get_tls_ctx(void);
-
-/* Log a message with file, function, and line information */
-void ceph_san_log(const char *file, const char *func, unsigned int line, const char *fmt, ...);
-
-/* Log registration structure */
-struct ceph_san_log_registration {
- const char *file; /* Source file - static pointer */
- const char *func; /* Source function - static pointer */
- unsigned int line; /* Line number */
- const char *fmt; /* Format string - static pointer */
- unsigned int id; /* Unique registration ID */
- size_t params_size; /* Size of all parameters when compacted */
-};
-
/* Log entry structure */
struct ceph_san_log_entry {
u64 debug_poison; /* Debug poison value */
u64 ts; /* Timestamp (jiffies) */
- unsigned int reg_id; /* Registration ID */
+ unsigned int line; /* Line number */
unsigned int len; /* Length of the message */
- char *buffer; /* Flexible array for inline buffer */
+ const char *file; /* Source file */
+ const char *func; /* Source function */
+ char *buffer; /* Flexible array for inline buffer */
};
/* TLS context structure */
/* Clean up the logging system */
void ceph_san_logger_cleanup(void);
-/* Register a log location and get unique ID */
-unsigned int ceph_san_log_register(const char *file, const char *func,
- unsigned int line, const char *fmt);
+/* Log a message */
+void ceph_san_log(const char *file, const char *func, unsigned int line, const char *fmt, ...);
-/* Log a message using registration ID */
-void ceph_san_log_with_id(unsigned int reg_id, const char *fmt, ...);
+/* Get current TLS context, creating if necessary */
+struct ceph_san_tls_ctx *ceph_san_get_tls_ctx(void);
/* Helper macro for logging */
#define CEPH_SAN_LOG(fmt, ...) \
- do { \
- static unsigned int __log_id = 0; \
- if (unlikely(!__log_id)) \
- __log_id = ceph_san_log_register(kbasename(__FILE__), __func__, \
- __LINE__, fmt); \
- ceph_san_log_with_id(__log_id, fmt, ##__VA_ARGS__); \
- } while (0)
+ ceph_san_log(kbasename(__FILE__), __func__, __LINE__, fmt, ##__VA_ARGS__)
/* Global logger instance */
extern struct ceph_san_logger g_logger;
-extern struct ceph_san_log_registration g_registrations[];
-
-extern void ceph_san_log_with_id_v(unsigned int reg_id, const char *fmt, va_list args);
#endif /* CEPH_SAN_LOGGER_H */
\ No newline at end of file
+++ /dev/null
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _CEPH_SAN_PACKER_H
-#define _CEPH_SAN_PACKER_H
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-
-/*
- * Elegant recursive packing macros
- *
- * These macros provide type-safe serialization and size calculation
- * for arbitrary data with minimal recursion complexity.
- */
-
-/* Special terminator value for recursion */
-#define _END _END_MARKER_
-
-/* SERIALIZATION MACROS - True recursion with direct pointer manipulation */
-
-#define SERIALIZE(buff, ...) _SERIALIZE(buff, ##__VA_ARGS__, _END)
-
-#define _SERIALIZE(buff, val, ...) \
- do { \
- typeof(val) *__ptr = (buff); \
- *__ptr = (val); \
- _SERIALIZE_CHECK((typeof(val) *)__ptr + 1, ##__VA_ARGS__); \
- } while(0)
-
-#define _SERIALIZE_CHECK(buff, first, ...) \
- _SERIALIZE_IF_NEMPTY(first)(buff, first, ##__VA_ARGS__)
-
-#define _SERIALIZE_IF_NEMPTY(first) \
- _SERIALIZE_TEST(first, _END, _SERIALIZE_EMPTY, _SERIALIZE_RECURSE)
-
-#define _SERIALIZE_TEST(a, b, empty, recurse) \
- _SERIALIZE_TEST_(a, b) (empty, recurse)
-
-#define _SERIALIZE_TEST_(a, b) \
- _SERIALIZE_IS_ ## b
-
-#define _SERIALIZE_IS__END(empty, recurse) empty
-#define _SERIALIZE_IS__END_MARKER_(empty, recurse) empty
-#define _SERIALIZE_IS_default(empty, recurse) recurse
-
-#define _SERIALIZE_EMPTY(...)
-#define _SERIALIZE_RECURSE(buff, val, ...) \
- _SERIALIZE(buff, val, ##__VA_ARGS__)
-
-/* SIZE CALCULATION MACROS - Elegant recursive calculation */
-
-#define CALC_SIZE(...) _CALC_SIZE(##__VA_ARGS__, _END)
-
-#define _CALC_SIZE(val, ...) \
- _CALC_SIZE_CHECK(sizeof(typeof(val)), ##__VA_ARGS__)
-
-#define _CALC_SIZE_CHECK(current_size, first, ...) \
- _CALC_SIZE_IF_NEMPTY(first)(current_size, first, ##__VA_ARGS__)
-
-#define _CALC_SIZE_IF_NEMPTY(first) \
- _CALC_SIZE_TEST(first, _END, _CALC_SIZE_FINAL, _CALC_SIZE_CONTINUE)
-
-#define _CALC_SIZE_TEST(a, b, final, cont) \
- _CALC_SIZE_TEST_(a, b) (final, cont)
-
-#define _CALC_SIZE_TEST_(a, b) \
- _CALC_SIZE_IS_ ## b
-
-#define _CALC_SIZE_IS__END(final, cont) final
-#define _CALC_SIZE_IS__END_MARKER_(final, cont) final
-#define _CALC_SIZE_IS_default(final, cont) cont
-
-#define _CALC_SIZE_FINAL(size) (size)
-#define _CALC_SIZE_CONTINUE(size, val, ...) \
- _CALC_SIZE_CHECK((size) + sizeof(typeof(val)), ##__VA_ARGS__)
-
-/* USAGE EXAMPLE:
- *
- * // Serialization
- * int buffer[3];
- * SERIALIZE(buffer, 1, 2, 3);
- *
- * // Size calculation
- * size_t size = CALC_SIZE(int_val, u64_val, char_val);
- *
- */
-
-#endif /* _CEPH_SAN_PACKER_H */
\ No newline at end of file
#include <linux/list.h>
#include <linux/sched.h>
#include <linux/string.h>
-#include <linux/seq_file.h>
#include <linux/ceph/ceph_san_logger.h>
#define CEPH_SAN_LOG_BATCH_MAX_FULL 128
struct ceph_san_logger g_logger;
EXPORT_SYMBOL(g_logger);
-/* Registration table */
-struct ceph_san_log_registration g_registrations[CEPH_SAN_LOG_MAX_REGISTRATIONS];
-EXPORT_SYMBOL(g_registrations);
-
-static unsigned int g_next_reg_id;
-static DEFINE_SPINLOCK(g_reg_lock);
-
static void *alloc_tls_ctx(void)
{
struct ceph_san_tls_ctx *ctx;
EXPORT_SYMBOL(ceph_san_get_tls_ctx);
/**
- * ceph_san_log - Log a message with file/func/line registration
- * @file: Source file name (static string)
- * @func: Function name (static string)
+ * ceph_san_log - Log a message
+ * @file: Source file name
* @line: Line number
- * @fmt: Format string (static string)
+ * @fmt: Format string
*
- * Registers the location on first call and reuses registration ID on subsequent calls.
- * The static global ID is stored in the first word of the format string buffer.
+ * Logs a message to the current TLS context's log buffer
*/
-void ceph_san_log(const char *file, const char *func, unsigned int line,
- const char *fmt, ...)
+void ceph_san_log(const char *file, const char *func, unsigned int line, const char *fmt, ...)
{
- static unsigned int *p_reg_id = NULL;
- unsigned int reg_id = 0;
+ /* Format the message into local buffer first */
+ char buf[256];
+ struct ceph_san_tls_ctx *ctx;
+ struct ceph_san_log_entry *entry;
va_list args;
+ u64 alloc;
+ int len, needed_size;
- /* Check if we've registered this location before */
- if (likely(p_reg_id)) {
- reg_id = *p_reg_id;
- } else {
- /* First call - register this location */
- reg_id = ceph_san_log_register(file, func, line, fmt);
- if (!reg_id) {
- /* Registration failed */
- pr_err("ceph_san_log: registration failed for %s:%s:%d\n",
- file, func, line);
- return;
- }
-
- /* Store registration ID in the format string's first word */
- p_reg_id = (unsigned int *)fmt;
- *p_reg_id = reg_id;
+ ctx = ceph_san_get_tls_ctx();
+ if (!ctx) {
+ pr_err("Failed to get TLS context\n");
+ return;
}
-
- /* Call the ID-based logger with the same arguments */
+
va_start(args, fmt);
- ceph_san_log_with_id_v(reg_id, fmt, args);
+ len = vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
+
+ needed_size = sizeof(*entry) + len + 1;
+ /* Allocate entry from pagefrag - We need a spinlock here to protect access iterators */
+ spin_lock_bh(&ctx->pf.lock);
+ alloc = cephsan_pagefrag_alloc(&ctx->pf, needed_size);
+ int loop_count = 0;
+ while (!alloc) {
+ entry = cephsan_pagefrag_get_ptr_from_tail(&ctx->pf);
+ if (loop_count++ >= 32) {
+ pr_err("ceph_san_log: pagefrag stats - head: %u, tail: %u, size: %u, free: %d\n",
+ ctx->pf.head, ctx->pf.tail,
+ CEPHSAN_PAGEFRAG_SIZE,
+ (ctx->pf.tail > ctx->pf.head) ?
+ ctx->pf.tail - ctx->pf.head :
+ CEPHSAN_PAGEFRAG_SIZE - (ctx->pf.head - ctx->pf.tail));
+
+ panic("ceph_san_log: failed to allocate entry after 8 retries");
+ }
+ if (entry->debug_poison != CEPH_SAN_LOG_ENTRY_POISON || entry->len == 0) {
+ pr_err("ceph_san_log: pagefrag corruption detected\n");
+ pr_err(" head: %u, tail: %u, size: %u\n",
+ ctx->pf.head, ctx->pf.tail, CEPHSAN_PAGEFRAG_SIZE);
+ pr_err(" active_elements: %u, alloc_count: %u\n",
+ ctx->pf.active_elements, ctx->pf.alloc_count);
+ pr_err(" entry poison: %llx, len: %u\n",
+ entry->debug_poison, entry->len);
+ pr_err(" wrap_to_end: %u, wrap_around: %u\n",
+ ctx->pf.wrap_to_end, ctx->pf.wrap_around);
+ BUG();
+ }
+ cephsan_pagefrag_free(&ctx->pf, entry->len);
+ alloc = cephsan_pagefrag_alloc(&ctx->pf, needed_size);
+ }
+ entry = cephsan_pagefrag_get_ptr(&ctx->pf, alloc);
+
+ /* Fill in entry details */
+ entry->debug_poison = CEPH_SAN_LOG_ENTRY_POISON;
+ entry->ts = jiffies;
+ entry->line = line;
+ entry->file = file;
+ entry->func = func;
+ if (unlikely(cephsan_pagefrag_is_wraparound(alloc))) {
+ entry->buffer = cephsan_pagefrag_get_ptr(&ctx->pf, 0);
+ } else {
+ entry->buffer = (char *)(entry + 1);
+ }
+ entry->len = cephsan_pagefrag_get_alloc_size(alloc);
+ spin_unlock_bh(&ctx->pf.lock);
+
+ /* Copy to entry buffer */
+ memcpy(entry->buffer, buf, len + 1);
+ entry->buffer[len] = '\0';
}
EXPORT_SYMBOL(ceph_san_log);
/* Initialize global state */
INIT_LIST_HEAD(&g_logger.contexts);
spin_lock_init(&g_logger.lock);
- g_next_reg_id = 1; /* Start IDs at 1, 0 is invalid */
/* Initialize allocation batch */
ret = ceph_san_batch_init(&g_logger.alloc_batch);
return entry;
}
-EXPORT_SYMBOL(ceph_san_log_iter_next);
-
-/**
- * ceph_san_log_register - Register a logging location
- * @file: Source file name (static string)
- * @func: Function name (static string)
- * @line: Line number
- * @fmt: Format string (static string)
- *
- * Registers a logging location to avoid storing the same information in each log entry.
- * Returns a registration ID or 0 on failure.
- */
-unsigned int ceph_san_log_register(const char *file, const char *func,
- unsigned int line, const char *fmt)
-{
- unsigned int reg_id;
- unsigned int params_size;
-
- if (!file || !func || !fmt)
- return 0;
-
- spin_lock_bh(&g_reg_lock);
-
- /* Check if this is a duplicate registration */
- for (reg_id = 1; reg_id < g_next_reg_id; reg_id++) {
- if (g_registrations[reg_id].file == file &&
- g_registrations[reg_id].func == func &&
- g_registrations[reg_id].line == line &&
- g_registrations[reg_id].fmt == fmt) {
- spin_unlock_bh(&g_reg_lock);
- return reg_id;
- }
- }
-
- /* Check if we have space for a new registration */
- if (g_next_reg_id >= CEPH_SAN_LOG_MAX_REGISTRATIONS) {
- spin_unlock_bh(&g_reg_lock);
- pr_err("Too many log registrations\n");
- return 0;
- }
-
- /* Calculate the size of parameters when compacted */
- params_size = strlen(file) + 1 + strlen(func) + 1 + sizeof(unsigned int) + strlen(fmt) + 1;
-
- /* Create a new registration */
- reg_id = g_next_reg_id++;
- g_registrations[reg_id].file = file;
- g_registrations[reg_id].func = func;
- g_registrations[reg_id].line = line;
- g_registrations[reg_id].fmt = fmt;
- g_registrations[reg_id].id = reg_id;
- g_registrations[reg_id].params_size = params_size;
-
- spin_unlock_bh(&g_reg_lock);
- return reg_id;
-}
-EXPORT_SYMBOL(ceph_san_log_register);
-
-/**
- * ceph_san_log_with_id_v - Log a message using registration ID with va_list
- * @reg_id: Registration ID
- * @fmt: Format string
- * @args: Variable argument list
- *
- * Logs a message using a pre-registered ID. The parameters are compacted
- * and stored efficiently.
- */
-void ceph_san_log_with_id_v(unsigned int reg_id, const char *fmt, va_list args)
-{
- char buf[256];
- struct ceph_san_tls_ctx *ctx;
- struct ceph_san_log_entry *entry;
- struct ceph_san_log_registration *reg;
- u64 alloc;
- int len, needed_size;
-
- /* Validate registration ID */
- if (!reg_id || reg_id >= g_next_reg_id) {
- pr_err("ceph_san_log: invalid registration ID %u\n", reg_id);
- return;
- }
-
- /* Get registration info */
- reg = &g_registrations[reg_id];
- if (reg->fmt != fmt) {
- pr_err("ceph_san_log: format string mismatch for ID %u\n", reg_id);
- return;
- }
-
- /* Get TLS context */
- ctx = ceph_san_get_tls_ctx();
- if (!ctx) {
- pr_err("Failed to get TLS context\n");
- return;
- }
-
- /* Format message with arguments */
- len = vsnprintf(buf, sizeof(buf), fmt, args);
-
- /* Allocate log entry with compacted parameters */
- needed_size = sizeof(*entry) + len + 1;
-
- spin_lock_bh(&ctx->pf.lock);
- alloc = cephsan_pagefrag_alloc(&ctx->pf, needed_size);
-
- /* Handle allocation failures by freeing older entries */
- int loop_count = 0;
- while (!alloc) {
- entry = cephsan_pagefrag_get_ptr_from_tail(&ctx->pf);
- if (loop_count++ >= 32) {
- pr_err("ceph_san_log: pagefrag stats - head: %u, tail: %u, size: %u, free: %d\n",
- ctx->pf.head, ctx->pf.tail,
- CEPHSAN_PAGEFRAG_SIZE,
- (ctx->pf.tail > ctx->pf.head) ?
- ctx->pf.tail - ctx->pf.head :
- CEPHSAN_PAGEFRAG_SIZE - (ctx->pf.head - ctx->pf.tail));
-
- panic("ceph_san_log: failed to allocate entry after 32 retries");
- }
-
- /* Check for corruption */
- if (entry->debug_poison != CEPH_SAN_LOG_ENTRY_POISON || entry->len == 0) {
- struct ceph_san_log_registration *reg = &g_registrations[entry->reg_id];
- pr_err("ceph_san_log: pagefrag corruption detected\n");
- pr_err(" head: %u, tail: %u, size: %u\n",
- ctx->pf.head, ctx->pf.tail, CEPHSAN_PAGEFRAG_SIZE);
- pr_err(" active_elements: %u, alloc_count: %u\n",
- ctx->pf.active_elements, ctx->pf.alloc_count);
- pr_err(" entry poison: %llx, len: %u\n",
- entry->debug_poison, entry->len);
- pr_err(" wrap_to_end: %u, wrap_around: %u\n",
- ctx->pf.wrap_to_end, ctx->pf.wrap_around);
- pr_err(" location: %s:%s:%u\n",
- reg->file, reg->func, reg->line);
- BUG();
- }
-
- /* Free the entry and try again */
- cephsan_pagefrag_free(&ctx->pf, entry->len);
- alloc = cephsan_pagefrag_alloc(&ctx->pf, needed_size);
- }
-
- /* Get pointer to the allocated entry */
- entry = cephsan_pagefrag_get_ptr(&ctx->pf, alloc);
-
- /* Fill in entry details */
- entry->debug_poison = CEPH_SAN_LOG_ENTRY_POISON;
- entry->ts = jiffies;
- entry->reg_id = reg_id;
-
- /* Handle buffer wraparound */
- if (unlikely(cephsan_pagefrag_is_wraparound(alloc))) {
- entry->buffer = cephsan_pagefrag_get_ptr(&ctx->pf, 0);
- } else {
- entry->buffer = (char *)(entry + 1);
- }
-
- entry->len = len;
- spin_unlock_bh(&ctx->pf.lock);
-
- /* Copy the formatted message to the buffer */
- memcpy(entry->buffer, buf, len + 1);
- entry->buffer[len] = '\0';
-}
-
-/**
- * ceph_san_log_with_id - Log a message using registration ID
- * @reg_id: Registration ID
- * @fmt: Format string
- *
- * Logs a message using a pre-registered ID. The parameters are compacted
- * and stored efficiently.
- */
-void ceph_san_log_with_id(unsigned int reg_id, const char *fmt, ...)
-{
- va_list args;
-
- va_start(args, fmt);
- ceph_san_log_with_id_v(reg_id, fmt, args);
- va_end(args);
-}
-EXPORT_SYMBOL(ceph_san_log_with_id);
\ No newline at end of file
+EXPORT_SYMBOL(ceph_san_log_iter_next);
\ No newline at end of file