[PATCH 01/12] define a new set of functions for error and debug logging

serue at us.ibm.com serue at us.ibm.com
Mon Nov 2 14:23:29 PST 2009


From: Serge E. Hallyn <serue at us.ibm.com>

The checkpoint context now includes buffers for an expanded
format and for messages to be written out.  A mutex protects
these buffers as they are being built up and written out.
ckpt_msg() will write general informative (debug) messages to
syslog and an optional user-provided logfile.  ckpt_err() will
write errors to the same places, and, if it is a checkpoint
operation, also to the checkpoint image.

(This is intended to implement Oren's suggestion verbatim)

Signed-off-by: Serge E. Hallyn <serue at us.ibm.com>
---
 checkpoint/sys.c                 |  175 ++++++++++++++++++++++++++++++++++++++
 include/linux/checkpoint.h       |   74 ++++++++++++++++
 include/linux/checkpoint_types.h |    5 +
 3 files changed, 254 insertions(+), 0 deletions(-)

diff --git a/checkpoint/sys.c b/checkpoint/sys.c
index 260a1ee..f50167a 100644
--- a/checkpoint/sys.c
+++ b/checkpoint/sys.c
@@ -248,6 +248,8 @@ static struct ckpt_ctx *ckpt_ctx_alloc(int fd, unsigned long uflags,
 	INIT_LIST_HEAD(&ctx->task_status);
 	spin_lock_init(&ctx->lock);
 #endif
+	
+	mutex_init(&ctx->msg_mutex);
 
 	err = -EBADF;
 	ctx->file = fget(fd);
@@ -339,6 +341,179 @@ int walk_task_subtree(struct task_struct *root,
 	return (ret < 0 ? ret : total);
 }
 
+void ckpt_msg_lock(struct ckpt_ctx *ctx)
+{
+	if (!ctx)
+		return;
+	mutex_lock(&ctx->msg_mutex);
+	ctx->msg[0] = '\0';
+	ctx->msglen = 1;
+}
+
+void ckpt_msg_unlock(struct ckpt_ctx *ctx)
+{
+	if (!ctx)
+		return;
+	mutex_unlock(&ctx->msg_mutex);
+}
+
+static inline int is_special_flag(char *s)
+{
+	if (*s == '%' && s[1] == '(' && s[2] != '\0' && s[3] == ')')
+		return 1;
+	return 0;
+}
+
+/*
+ * _ckpt_generate_fmt - handle the special flags in the enhanced format
+ * strings used by checkpoint/restart error messages.
+ * @ctx: checkpoint context
+ * @fmt: message format
+ *
+ * The special flags are surrounded by %() to help them visually stand
+ * out.  For instance, %(O) means an objref.  The following special
+ * flags are recognized:
+ *	E: error
+ *	O: objref
+ *	P: pointer
+ *	T: task
+ *	S: string
+ *	V: variable
+ *
+ * %(E) will be expanded to "[err %d]".  Likewise O, P, S, and V, will
+ * also expand to format flags requiring an argument to the subsequent
+ * sprintf or printk.  T will be expanded to a string with no flags,
+ * requiring no further arguments.
+ *
+ * These do not accept any extra flags (i.e. min field width, precision,
+ * etc).
+ *
+ * The caller of ckpt_write_err() and _ckpt_write_err() must provide
+ * the additional variabes, in order, to match the @fmt (except for
+ * the T key), e.g.:
+ *
+ *	ckpt_write_err(ctx, "%(T)FILE flags %d %O %E\n", flags, objref, err);
+ *
+ * Must be called with ctx->fmt_buf_lock held.  The expanded format
+ * will be placed in ctx->fmt_buf.
+ */
+void _ckpt_generate_fmt(struct ckpt_ctx *ctx, char *fmt)
+{
+	char *s = ctx->fmt;
+	int len = 0;
+	int first = 1;
+
+	for (; *fmt && len < CKPT_MSG_LEN; fmt++) {
+		if (!is_special_flag(fmt)) {
+			s[len++] = *fmt;
+			continue;
+		}
+		if (!first)
+			s[len++] = ' ';
+		else
+			first = 0;
+		switch (fmt[2]) {
+		case 'E':
+			len += snprintf(s+len, CKPT_MSG_LEN-len, "[err %%d]");
+			break;
+		case 'O':
+			len += snprintf(s+len, CKPT_MSG_LEN-len, "[obj %%d]");
+			break;
+		case 'P':
+			len += snprintf(s+len, CKPT_MSG_LEN-len, "[ptr %%p]");
+			break;
+		case 'V':
+			len += snprintf(s+len, CKPT_MSG_LEN-len, "[sym %%pS]");
+			break;
+		case 'S':
+			len += snprintf(s+len, CKPT_MSG_LEN-len, "[str %%s]");
+			break;
+		case 'T':
+			if (ctx->tsk)
+				len += snprintf(s+len, CKPT_MSG_LEN-len,
+					"[pid %d tsk %s]",
+					task_pid_vnr(ctx->tsk), ctx->tsk->comm);
+			else
+				len += snprintf(s+len, CKPT_MSG_LEN-len,
+					"[pid -1 tsk NULL]");
+			break;
+		default:
+			printk(KERN_ERR "c/r: bad format specifier %c\n",
+					fmt[2]);
+			BUG();
+		}
+		fmt += 3;
+	}
+}
+
+static void _ckpt_msg_appendv(struct ckpt_ctx *ctx, char *fmt, va_list ap)
+{
+	int len = ctx->msglen;
+
+	len += vsnprintf(&ctx->msg[len], CKPT_MSG_LEN-len, fmt, ap);
+	if (len > CKPT_MSG_LEN) {
+		len = CKPT_MSG_LEN;
+		ctx->msg[CKPT_MSG_LEN-1] = '\0';
+	}
+	ctx->msglen = len;
+}
+
+void _ckpt_msg_append(struct ckpt_ctx *ctx, char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	_ckpt_msg_appendv(ctx, fmt, ap);
+	va_end(ap);
+}
+
+void _ckpt_msg_complete(struct ckpt_ctx *ctx)
+{
+	int ret;
+
+	if (ctx->kflags & CKPT_CTX_CHECKPOINT) {
+		ret = ckpt_write_obj_type(ctx, NULL, 0, CKPT_HDR_ERROR);
+		if (!ret)
+			ret = ckpt_write_string(ctx, ctx->msg, ctx->msglen);
+		if (ret < 0)
+			printk(KERN_NOTICE "c/r: error string unsaved (%d): %s\n",
+			       ret, ctx->msg+1);
+	}
+#if 0
+	if (ctx->logfile) {
+		mm_segment_t fs = get_fs();
+		set_fs(KERNEL_DS);
+		ret = _ckpt_kwrite(ctx->logfile, ctx->msg+1, ctx->msglen-1);
+		set_fs(fs);
+	}
+#endif
+#ifdef CONFIG_CHECKPOINT_DEBUG
+	printk(KERN_DEBUG "%s", ctx->msg+1);
+#endif
+}
+
+#define __do_ckpt_msg(ctx, fmt) do {		\
+	va_list ap;				\
+	_ckpt_generate_fmt(ctx, fmt);		\
+	va_start(ap, fmt);			\
+	_ckpt_msg_appendv(ctx, ctx->fmt, ap);	\
+	va_end(ap);				\
+} while (0)
+
+void _do_ckpt_msg(struct ckpt_ctx *ctx, char *fmt, ...)
+{
+	__do_ckpt_msg(ctx, fmt);
+}
+
+void do_ckpt_msg(struct ckpt_ctx *ctx, char *fmt, ...)
+{
+	if (!ctx) return;
+
+	ckpt_msg_lock(ctx);
+	__do_ckpt_msg(ctx, fmt);
+	_ckpt_msg_complete(ctx);
+	ckpt_msg_unlock(ctx);
+}
 
 /**
  * sys_checkpoint - checkpoint a container
diff --git a/include/linux/checkpoint.h b/include/linux/checkpoint.h
index dfcb59b..3083e06 100644
--- a/include/linux/checkpoint.h
+++ b/include/linux/checkpoint.h
@@ -74,6 +74,9 @@ extern int ckpt_write_string(struct ckpt_ctx *ctx, char *str, int len);
  * form: "[PREFMT]: @fmt", where PREFMT is constructed from @fmt0. See
  * checkpoint/checkpoint.c:__ckpt_generate_fmt() for details.
  */
+/*
+ * This is deprecated, replaced by ckpt_err() and ckpt_err_locked()
+ */
 extern void __ckpt_write_err(struct ckpt_ctx *ctx, char *fmt0, char *fmt, ...);
 extern int ckpt_write_err(struct ckpt_ctx *ctx, char *fmt0, char *fmt, ...);
 
@@ -342,6 +345,9 @@ static inline int ckpt_validate_errno(int errno)
 extern void restore_debug_free(struct ckpt_ctx *ctx);
 extern unsigned long ckpt_debug_level;
 
+/*
+ * This is deprecated
+ */
 /* use this to select a specific debug level */
 #define _ckpt_debug(level, fmt, args...)				\
 	do {								\
@@ -365,11 +371,79 @@ extern unsigned long ckpt_debug_level;
 
 static inline void restore_debug_free(struct ckpt_ctx *ctx) {}
 
+/*
+ * This is deprecated
+ */
 #define _ckpt_debug(level, fmt, args...)	do { } while (0)
 #define ckpt_debug(fmt, args...)		do { } while (0)
 
 #endif /* CONFIG_CHECKPOINT_DEBUG */
 
+/*
+ * prototypes for the new logging api
+ */
+
+extern void ckpt_msg_lock(struct ckpt_ctx *ctx);
+extern void ckpt_msg_unlock(struct ckpt_ctx *ctx);
+
+/*
+ * Expand fmt into ctx->fmt.
+ * This expands enhanced format flags such as %(T), which takes no
+ * arguments, and %(E), which will require a properly positioned
+ * int error argument.  Flags include:
+ *   T: Task
+ *   E: Errno
+ *   O: Objref
+ *   P: Pointer
+ *   S: string
+ *   V: Variable (symbol)
+ * May be called under spinlock.
+ * Must be called under ckpt_msg_lock.
+ */
+extern void _ckpt_generate_fmt(struct ckpt_ctx *ctx, char *fmt);
+/*
+ * Append formatted msg to ctx->msg[ctx->msg_len].
+ * Must be called after expanding format.
+ * May be called under spinlock.
+ * Must be called under ckpt_msg_lock().
+ */
+extern void _ckpt_msg_append(struct ckpt_ctx *ctx, char *fmt, ...);
+
+/*
+ * Write ctx->msg to all relevant places.
+ * Must not be called under spinlock.
+ * Must be called under ckpt_msg_lock().
+ */
+extern void _ckpt_msg_complete(struct ckpt_ctx *ctx);
+
+/*
+ * Append an enhanced formatted message to ctx->msg.
+ * This will not write the message out to the applicable files, so
+ * the caller will have to use _ckpt_msg_complete() to finish up.
+ * Must be called with ckpt_msg_lock held.
+ */
+extern void _do_ckpt_msg(struct ckpt_ctx *ctx, char *fmt, ...);
+
+/*
+ * Append an enhanced formatted message to ctx->msg.
+ * This will take the ckpt_msg_lock and also write the message out
+ * to the applicable files by calling _ckpt_msg_complete().
+ * Must not be called under spinlock.
+ */
+extern void do_ckpt_msg(struct ckpt_ctx *ctx, char *fmt, ...);
+
+#define ckpt_err(ctx, fmt, args...) do {					\
+	do_ckpt_msg(ctx, "[Error at %s:%d]" fmt, __func__, __LINE__,  ##args);	\
+} while (0)
+
+
+#define _ckpt_err(ctx, fmt, args...) do { \
+	_do_ckpt_msg(ctx, "[ error %s:%d]" fmt, __func__, __LINE__, ##args);	\
+} while (0)
+
+/* ckpt_logmsg() or ckpt_msg() will do do_ckpt_msg with an added
+ * prefix */
+
 #endif /* CONFIG_CHECKPOINT */
 #endif /* __KERNEL__ */
 
diff --git a/include/linux/checkpoint_types.h b/include/linux/checkpoint_types.h
index 5cc11d9..6771b9f 100644
--- a/include/linux/checkpoint_types.h
+++ b/include/linux/checkpoint_types.h
@@ -85,6 +85,11 @@ struct ckpt_ctx {
 	struct list_head task_status;   /* list of status for each task */
 	spinlock_t lock;
 #endif
+#define CKPT_MSG_LEN 1024
+	char fmt[CKPT_MSG_LEN];
+	char msg[CKPT_MSG_LEN];
+	int msglen;
+	struct mutex msg_mutex;
 };
 
 #endif /* __KERNEL__ */
-- 
1.6.1



More information about the Containers mailing list