Add fetch.c/fetch.h, a module for fetching function arguments

- this is now a thin wrapper over gimme_arg, ideally the backends will
  eventually use this right interface

- in display_args.c, strip one layer of pointer wrapping, which is now
  done in output.c
diff --git a/ChangeLog b/ChangeLog
index 4f20aa9..0324341 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,18 @@
 2012-01-06  Petr Machata  <pmachata@redhat.com>
 
+	* fetch.c, fetch.h: New module.
+	* common.h (enum tof): Move to fetch.h
+	(struct output_state): New struct
+	(struct callstack_element.fetch_context, .out): New fields
+	* handle_event.c (handle_clone): Clone fetch_context's
+	* output.c (build_default_prototype): New function
+	(fetch_simple_param, fetch_param_stop, fetch_one_param)
+	(fetch_params, output_one, output_params): New functions.
+	(output_left, output_right): Do the parameter output using the
+	above functions.
+
+2012-01-06  Petr Machata  <pmachata@redhat.com>
+
 	* common.h (enum tof.LT_TOF_NONE, .LT_TOF_STRUCT): Drop.
 	* output.c: Adjust.
 
diff --git a/Makefile.am b/Makefile.am
index 3c8cb25..90165d0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -34,6 +34,7 @@
 	value.c \
 	value_dict.c \
 	expr.c \
+	fetch.c \
 	vect.c \
 
 libltrace_la_LIBADD = \
@@ -76,6 +77,7 @@
 	value.h \
 	value_dict.h \
 	expr.h \
+	fetch.h \
 	vect.h \
 
 dist_man1_MANS = \
diff --git a/common.h b/common.h
index 258ad8b..acff84b 100644
--- a/common.h
+++ b/common.h
@@ -50,13 +50,6 @@
 
 extern int exiting;  /* =1 if we have to exit ASAP */
 
-enum tof {
-	LT_TOF_FUNCTION,	/* A real library function */
-	LT_TOF_FUNCTIONR,	/* Return from a real library function */
-	LT_TOF_SYSCALL,		/* A syscall */
-	LT_TOF_SYSCALLR,	/* Return from a syscall */
-};
-
 typedef struct Function Function;
 struct Function {
 	const char * name;
@@ -140,8 +133,6 @@
 extern void continue_after_syscall(Process *proc, int sysnum, int ret_p);
 extern void continue_after_breakpoint(struct Process *proc, struct breakpoint *sbp);
 extern void continue_after_vfork(Process * proc);
-extern long gimme_arg(enum tof type, Process *proc, int arg_num,
-		      struct arg_type_info *info);
 extern size_t umovebytes (Process *proc, void * addr, void * laddr, size_t count);
 extern int ffcheck(void * maddr);
 extern void * sym2addr(Process *, struct library_symbol *);
diff --git a/display_args.c b/display_args.c
index 8848b35..4a1747d 100644
--- a/display_args.c
+++ b/display_args.c
@@ -35,9 +35,6 @@
 #include "type.h"
 #include "common.h"
 
-static int format_argument_2(FILE *stream, struct value *value,
-			     struct value_dict *arguments);
-
 #define READER(NAME, TYPE)						\
 	static int							\
 	NAME(struct value *value, TYPE *ret, struct value_dict *arguments) \
@@ -235,7 +232,7 @@
 		struct value element;
 		if (value_init_element(&element, value, i) < 0)
 			return -1;
-		int o = format_argument_2(stream, &element, arguments);
+		int o = format_argument(stream, &element, arguments);
 		if (o < 0)
 			return -1;
 		written += o;
@@ -251,7 +248,7 @@
 	struct value element;
 	if (value_init_deref(&element, value) < 0)
 		return -1;
-	return format_argument_2(stream, &element, arguments);
+	return format_argument(stream, &element, arguments);
 }
 
 /*
@@ -300,7 +297,7 @@
 			return -1;
 		if (value_is_zero(&element, arguments)) /* XXX emulate ZERO */
 			break;
-		int o = format_argument_2(stream, &element, arguments);
+		int o = format_argument(stream, &element, arguments);
 		if (o < 0)
 			return -1;
 		written += o;
@@ -313,9 +310,8 @@
 	return written;
 }
 
-static int
-format_argument_2(FILE *stream, struct value *value,
-		  struct value_dict *arguments)
+int
+format_argument(FILE *stream, struct value *value, struct value_dict *arguments)
 {
 	struct expr_node *length = NULL;
 	switch (value->type->type) {
@@ -384,7 +380,7 @@
 		value_clone(&tmp, value);
 		value_set_type(&tmp, info, 0);
 
-		int ret = format_argument_2(stream, &tmp, arguments);
+		int ret = format_argument(stream, &tmp, arguments);
 
 		value_destroy(&tmp);
 		type_destroy(&info[0]);
@@ -400,30 +396,3 @@
 	}
 	abort();
 }
-
-int
-format_argument(FILE *stream, struct value *value, struct value_dict *arguments)
-{
-	/* Arrays decay into pointers for purposes of argument
-	 * passing.  Before the proper support for this lands, wrap
-	 * top-level arrays in pointers here.  */
-	if (value->type->type == ARGTYPE_ARRAY) {
-
-		struct arg_type_info info;
-		type_init_pointer(&info, value->type, 0);
-
-		struct value tmp;
-		value_clone(&tmp, value);
-		value_set_type(&tmp, &info, 0);
-
-		int ret = format_argument_2(stream, &tmp, arguments);
-
-		value_destroy(&tmp);
-		type_destroy(&info);
-
-		return ret;
-
-	} else {
-		return format_argument_2(stream, value, arguments);
-	}
-}
diff --git a/fetch.c b/fetch.c
new file mode 100644
index 0000000..f68b4e5
--- /dev/null
+++ b/fetch.c
@@ -0,0 +1,130 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "fetch.h"
+#include "value.h"
+#include "arch.h"
+
+#ifdef ARCH_HAVE_FETCH_ARG
+struct fetch_context *arch_fetch_arg_init(enum tof type, struct Process *proc,
+					  struct arg_type_info *ret_info);
+
+struct fetch_context *arch_fetch_arg_clone(struct Process *proc,
+					   struct fetch_context *context);
+
+int arch_fetch_arg_next(struct fetch_context *ctx, enum tof type,
+			struct Process *proc, struct arg_type_info *info,
+			struct value *valuep);
+
+int arch_fetch_retval(struct fetch_context *ctx, enum tof type,
+		      struct Process *proc, struct arg_type_info *info,
+		      struct value *valuep);
+
+void arch_fetch_arg_done(struct fetch_context *context);
+
+#else
+/* Fall back to gimme_arg.  */
+
+long gimme_arg(enum tof type, struct Process *proc, int arg_num,
+	       struct arg_type_info *info);
+
+struct fetch_context {
+	int argnum;
+};
+
+struct fetch_context *
+arch_fetch_arg_init(enum tof type, struct Process *proc,
+		    struct arg_type_info *ret_info)
+{
+	return calloc(sizeof(struct fetch_context), 1);
+}
+
+struct fetch_context *
+arch_fetch_arg_clone(struct Process *proc, struct fetch_context *context)
+{
+	struct fetch_context *ret = malloc(sizeof(*ret));
+	if (ret == NULL)
+		return NULL;
+	return memcpy(ret, context, sizeof(*ret));
+}
+
+int
+arch_fetch_arg_next(struct fetch_context *context, enum tof type,
+		    struct Process *proc,
+		    struct arg_type_info *info, struct value *valuep)
+{
+	long l = gimme_arg(type, proc, context->argnum++, info);
+	value_set_long(valuep, l);
+	return 0;
+}
+
+int
+arch_fetch_retval(struct fetch_context *context, enum tof type,
+		  struct Process *proc,
+		  struct arg_type_info *info, struct value *valuep)
+{
+	long l = gimme_arg(type, proc, -1, info);
+	value_set_long(valuep, l);
+	return 0;
+}
+
+void
+arch_fetch_arg_done(struct fetch_context *context)
+{
+	free(context);
+}
+#endif
+
+struct fetch_context *
+fetch_arg_init(enum tof type, struct Process *proc,
+	       struct arg_type_info *ret_info)
+{
+	return arch_fetch_arg_init(type, proc, ret_info);
+}
+
+struct fetch_context *
+fetch_arg_clone(struct Process *proc, struct fetch_context *context)
+{
+	return arch_fetch_arg_clone(proc, context);
+}
+
+int
+fetch_arg_next(struct fetch_context *context, enum tof type,
+	       struct Process *proc,
+	       struct arg_type_info *info, struct value *valuep)
+{
+	return arch_fetch_arg_next(context, type, proc, info, valuep);
+}
+
+int
+fetch_retval(struct fetch_context *context, enum tof type,
+	     struct Process *proc,
+	     struct arg_type_info *info, struct value *valuep)
+{
+	return arch_fetch_retval(context, type, proc, info, valuep);
+}
+
+void
+fetch_arg_done(struct fetch_context *context)
+{
+	return arch_fetch_arg_done(context);
+}
diff --git a/fetch.h b/fetch.h
new file mode 100644
index 0000000..6a5385c
--- /dev/null
+++ b/fetch.h
@@ -0,0 +1,64 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef FETCH_H
+#define FETCH_H
+
+#include "forward.h"
+
+/* XXX isn't SYSCALL TOF just a different ABI?  Maybe we needed to
+ * support variant ABIs all along.  */
+enum tof {
+	LT_TOF_FUNCTION,	/* A real library function */
+	LT_TOF_FUNCTIONR,	/* Return from a real library function */
+	LT_TOF_SYSCALL,		/* A syscall */
+	LT_TOF_SYSCALLR,	/* Return from a syscall */
+};
+
+/* The contents of the structure is defined by the back end.  */
+struct fetch_context;
+
+/* Initialize argument fetching.  Returns NULL on failure.  RET_INFO
+ * is the return type of the function.  */
+struct fetch_context *fetch_arg_init(enum tof type, struct Process *proc,
+				     struct arg_type_info *ret_info);
+
+/* Make a clone of context.  */
+struct fetch_context *fetch_arg_clone(struct Process *proc,
+				      struct fetch_context *context);
+
+/* Load next argument.  The function returns 0 on success or a
+ * negative value on failure.  The extracted value is stored in
+ * *VALUEP.  */
+int fetch_arg_next(struct fetch_context *context, enum tof type,
+		   struct Process *proc,
+		   struct arg_type_info *info, struct value *valuep);
+
+/* Load return value.  The function returns 0 on success or a negative
+ * value on failure.  The extracted value is stored in *VALUEP.  */
+int fetch_retval(struct fetch_context *context, enum tof type,
+		 struct Process *proc,
+		 struct arg_type_info *info, struct value *valuep);
+
+/* Destroy fetch context.  CONTEXT shall be the same memory location
+ * that was passed to fetch_arg_next.  */
+void fetch_arg_done(struct fetch_context *context);
+
+#endif /* FETCH_H */
diff --git a/handle_event.c b/handle_event.c
index 7d1eb61..ffce2ff 100644
--- a/handle_event.c
+++ b/handle_event.c
@@ -37,9 +37,9 @@
 #include "common.h"
 #include "value_dict.h"
 #include "breakpoint.h"
-#include "common.h"
 #include "library.h"
 #include "proc.h"
+#include "fetch.h"
 
 static void handle_signal(Event *event);
 static void handle_exit(Event *event);
diff --git a/output.c b/output.c
index e1b3a2a..ee444d5 100644
--- a/output.c
+++ b/output.c
@@ -31,6 +31,7 @@
 #include <time.h>
 #include <sys/time.h>
 #include <unistd.h>
+#include <errno.h>
 
 #include "common.h"
 #include "proc.h"
@@ -38,6 +39,7 @@
 #include "type.h"
 #include "value.h"
 #include "value_dict.h"
+#include "fetch.h"
 
 /* TODO FIXME XXX: include in common.h: */
 extern struct timeval current_time_spent;
@@ -125,27 +127,50 @@
 	}
 }
 
+/* The default prototype is: long X(long, long, long, long).  */
+static Function *
+build_default_prototype(void)
+{
+	Function *ret = malloc(sizeof(*ret));
+	size_t i = 0;
+	if (ret == NULL)
+		goto err;
+	memset(ret, 0, sizeof(*ret));
+
+	struct arg_type_info *unknown_type = type_get_simple(ARGTYPE_UNKNOWN);
+
+	ret->return_info = unknown_type;
+
+	ret->num_params = 4;
+	for (i = 0; i < (size_t)ret->num_params; ++i)
+		ret->arg_info[i] = unknown_type;
+
+	return ret;
+
+err:
+	report_global_error("malloc: %s", strerror(errno));
+	free(ret);
+
+	return NULL;
+}
+
 static Function *
 name2func(char const *name) {
 	Function *tmp;
 	const char *str1, *str2;
 
-	tmp = list_of_functions;
-	while (tmp) {
-#ifdef USE_DEMANGLE
-		str1 = options.demangle ? my_demangle(tmp->name) : tmp->name;
-		str2 = options.demangle ? my_demangle(name) : name;
-#else
+	for (tmp = list_of_functions; tmp != NULL; tmp = tmp->next) {
 		str1 = tmp->name;
 		str2 = name;
-#endif
-		if (!strcmp(str1, str2)) {
-
+		if (!strcmp(str1, str2))
 			return tmp;
-		}
-		tmp = tmp->next;
 	}
-	return NULL;
+
+	static Function *def = NULL;
+	if (def == NULL)
+		def = build_default_prototype();
+
+	return def;
 }
 
 void
@@ -182,15 +207,128 @@
 	}
 }
 
+static int
+account_output(int o)
+{
+	if (o < 0)
+		return -1;
+	current_column += o;
+	return 0;
+}
+
+static int
+output_error(void)
+{
+	return account_output(fprintf(options.output, "?"));
+}
+
+static int
+fetch_simple_param(enum tof type, Process *proc, struct fetch_context *context,
+		   struct value_dict *arguments, struct arg_type_info *info,
+		   struct value *valuep)
+{
+	/* Arrays decay into pointers per C standard.  We check for
+	 * this here, because here we also capture arrays that come
+	 * from parameter packs.  */
+	int own = 0;
+	if (info->type == ARGTYPE_ARRAY) {
+		struct arg_type_info *tmp = malloc(sizeof(*tmp));
+		if (tmp != NULL) {
+			type_init_pointer(tmp, info, 0);
+			info = tmp;
+			own = 1;
+		}
+	}
+
+	struct value value;
+	value_init(&value, proc, NULL, info, own);
+	if (fetch_arg_next(context, type, proc, info, &value) < 0)
+		return -1;
+
+	if (val_dict_push_next(arguments, &value) < 0) {
+		value_destroy(&value);
+		return -1;
+	}
+
+	if (valuep != NULL)
+		*valuep = value;
+
+	return 0;
+}
+
+static void
+fetch_param_stop(struct value_dict *arguments, ssize_t *params_leftp)
+{
+	if (*params_leftp == -1)
+		*params_leftp = val_dict_count(arguments);
+}
+
+static int
+fetch_one_param(enum tof type, Process *proc, struct fetch_context *context,
+		struct value_dict *arguments, struct arg_type_info *info,
+		ssize_t *params_leftp)
+{
+	return fetch_simple_param(type, proc, context, arguments,
+				  info, NULL);
+}
+
+static int
+fetch_params(enum tof type, Process *proc, struct fetch_context *context,
+	     struct value_dict *arguments, Function *func, ssize_t *params_leftp)
+{
+	size_t i;
+	for (i = 0; i < (size_t)func->num_params; ++i) {
+		if (i == (size_t)(func->num_params - func->params_right))
+			fetch_param_stop(arguments, params_leftp);
+		if (fetch_one_param(type, proc, context, arguments,
+				    func->arg_info[i], params_leftp) < 0)
+			return -1;
+	}
+
+	/* Implicit stop at the end of parameter list.  */
+	fetch_param_stop(arguments, params_leftp);
+	return 0;
+}
+
+static int
+output_one(struct value *val, struct value_dict *arguments)
+{
+	int o = format_argument(options.output, val, arguments);
+	if (account_output(o) < 0) {
+		if (output_error() < 0)
+			return -1;
+		o = 1;
+	}
+	return o;
+}
+
+static int
+output_params(struct value_dict *arguments, size_t start, size_t end,
+	      int *need_delimp)
+{
+	size_t i;
+	int need_delim = *need_delimp;
+	for (i = start; i < end; ++i) {
+		if (need_delim
+		    && account_output(fprintf(options.output, ", ")) < 0)
+			return -1;
+		struct value *value = val_dict_get_num(arguments, i);
+		if (value == NULL)
+			return -1;
+		need_delim = output_one(value, arguments);
+		if (need_delim < 0)
+			return -1;
+	}
+	*need_delimp = need_delim;
+	return 0;
+}
+
 void
 output_left(enum tof type, struct Process *proc,
 	    struct library_symbol *libsym)
 {
 	const char *function_name = libsym->name;
 	Function *func;
-	static struct arg_type_info *arg_unknown = NULL;
-	if (arg_unknown == NULL)
-	    arg_unknown = lookup_prototype(ARGTYPE_UNKNOWN);
 
 	if (options.summary) {
 		return;
@@ -201,74 +339,46 @@
 	}
 	current_proc = proc;
 	current_depth = proc->callstack_depth;
-	begin_of_line(type, type == LT_TOF_FUNCTION, 1);
+	begin_of_line(proc, type == LT_TOF_FUNCTION, 1);
 	if (!options.hide_caller && libsym->lib != NULL
 	    && libsym->plt_type != LS_TOPLT_NONE)
 		current_column += fprintf(options.output, "%s->",
 					  libsym->lib->soname);
+
+	const char *name = function_name;
 #ifdef USE_DEMANGLE
-	current_column +=
-		fprintf(options.output, "%s(",
-			(options.demangle
-			 ? my_demangle(function_name) : function_name));
-#else
-	current_column += fprintf(options.output, "%s(", function_name);
+	if (options.demangle)
+		name = my_demangle(function_name);
 #endif
+	if (account_output(fprintf(options.output, "%s(", name)) < 0)
+		return;
 
 	func = name2func(function_name);
+	if (func == NULL)
+		return;
 
+	struct fetch_context *context = fetch_arg_init(type, proc,
+						       func->return_info);
 	struct value_dict *arguments = malloc(sizeof(*arguments));
 	if (arguments == NULL)
 		return;
 	val_dict_init(arguments);
 
-	int num, right;
-	if (!func) {
-		int i;
-		for (i = 0; i < 4; i++) {
-			long l = gimme_arg(type, proc, i, arg_unknown);
-			struct value val;
-			value_init(&val, proc, NULL, arg_unknown, 0);
-			value_set_long(&val, l);
-			val_dict_push_next(arguments, &val);
-		}
-		right = 0;
-		num = 4;
-	} else {
-		int i;
-		for (i = 0; i < func->num_params; i++) {
-			long l = gimme_arg(type, proc, i, func->arg_info[i]);
-			struct value val;
-			value_init(&val, proc, NULL, func->arg_info[i], 0);
-			value_set_long(&val, l);
-			val_dict_push_next(arguments, &val);
-		}
-		right = func->params_right;
-		num = func->num_params;
-	}
-
-	int i;
-	for (i = 0; i < num - right - 1; i++) {
-		current_column +=
-			format_argument(options.output,
-					val_dict_get_num(arguments, i),
-					arguments);
-		current_column += fprintf(options.output, ", ");
-	}
-
-	if (num > right) {
-		current_column +=
-			format_argument(options.output,
-					val_dict_get_num(arguments, i),
-					arguments);
-		if (right) {
-			current_column += fprintf(options.output, ", ");
-		}
+	ssize_t params_left = -1;
+	int need_delim = 0;
+	if (fetch_params(type, proc, context, arguments, func, &params_left) < 0
+	    || output_params(arguments, 0, params_left, &need_delim) < 0) {
+		val_dict_destroy(arguments);
+		fetch_arg_done(context);
+		return;
 	}
 
 	struct callstack_element *stel
 		= &proc->callstack[proc->callstack_depth - 1];
+	stel->fetch_context = context;
 	stel->arguments = arguments;
+	stel->out.params_left = params_left;
+	stel->out.need_delim = need_delim;
 }
 
 void
@@ -276,9 +386,8 @@
 {
 	const char *function_name = libsym->name;
 	Function *func = name2func(function_name);
-	static struct arg_type_info *arg_unknown = NULL;
-	if (arg_unknown == NULL)
-	    arg_unknown = lookup_prototype(ARGTYPE_UNKNOWN);
+	if (func == NULL)
+		return;
 
 	if (options.summary) {
 		struct opt_c_struct *st;
@@ -333,44 +442,41 @@
 	struct callstack_element *stel
 		= &proc->callstack[proc->callstack_depth - 1];
 
-	struct value retval;
-	struct arg_type_info *return_info = arg_unknown;
-	if (func != NULL)
-		return_info = func->return_info;
-	long l = gimme_arg(type, proc, -1, return_info);
-	value_init(&retval, proc, NULL, return_info, 0);
-	value_set_long(&retval, l);
-	val_dict_push_named(stel->arguments, &retval, "retval", 0);
+	struct fetch_context *context = stel->fetch_context;
 
-	if (!func) {
-		current_column += fprintf(options.output, ") ");
-		tabto(options.align - 1);
-		fprintf(options.output, "= ");
-	} else {
-		int i;
-		for (i = func->num_params - func->params_right;
-		     i < func->num_params - 1; i++) {
-			current_column +=
-				format_argument(options.output,
-						val_dict_get_num
-							(stel->arguments, i),
-						stel->arguments);
-			current_column += fprintf(options.output, ", ");
+	/* Fetch & enter into dictionary the retval first, so that
+	 * other values can use it in expressions.  */
+	struct value retval;
+	int own_retval = 0;
+	if (context != NULL) {
+		value_init(&retval, proc, NULL, func->return_info, 0);
+		own_retval = 1;
+		if (fetch_retval(context, type, proc, func->return_info,
+				 &retval) == 0) {
+			if (stel->arguments != NULL
+			    && val_dict_push_named(stel->arguments, &retval,
+						   "retval", 0) == 0)
+				own_retval = 0;
 		}
-		if (func->params_right) {
-			current_column +=
-				format_argument(options.output,
-						val_dict_get_num
-							(stel->arguments, i),
-						stel->arguments);
-		}
-		current_column += fprintf(options.output, ") ");
-		tabto(options.align - 1);
-		fprintf(options.output, "= ");
 	}
 
-	format_argument(options.output, &retval, stel->arguments);
+	if (stel->arguments != NULL)
+		output_params(stel->arguments, stel->out.params_left,
+			      val_dict_count(stel->arguments),
+			      &stel->out.need_delim);
+
+	current_column += fprintf(options.output, ") ");
+	tabto(options.align - 1);
+	fprintf(options.output, "= ");
+
+	output_one(&retval, stel->arguments);
+
+	if (own_retval)
+		value_destroy(&retval);
+
 	val_dict_destroy(stel->arguments);
+	free(stel->arguments);
+	fetch_arg_done(context);
 
 	if (opt_T) {
 		fprintf(options.output, " <%lu.%06d>",
diff --git a/output.h b/output.h
index 2db9d27..481a385 100644
--- a/output.h
+++ b/output.h
@@ -18,6 +18,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  * 02110-1301 USA
  */
+#include "fetch.h"
 
 #include "forward.h"
 
diff --git a/proc.c b/proc.c
index 7672ae8..cd8f6cd 100644
--- a/proc.c
+++ b/proc.c
@@ -291,11 +291,26 @@
 
 	size_t i;
 	for (i = 0; i < retp->callstack_depth; ++i) {
+		struct fetch_context *ctx = retp->callstack[i].fetch_context;
+		if (ctx != NULL) {
+			struct fetch_context *nctx = fetch_arg_clone(p, ctx);
+			if (nctx == NULL) {
+				int j;
+			release1:
+				for (j = 0; j < i; ++j) {
+					nctx = retp->callstack[i].fetch_context;
+					fetch_arg_done(nctx);
+					retp->callstack[i].fetch_context = NULL;
+				}
+				goto fail2;
+			}
+			retp->callstack[i].fetch_context = nctx;
+		}
+
 		struct value_dict *args = retp->callstack[i].arguments;
 		if (args != NULL) {
 		fail3:
 			struct value_dict *nargs = malloc(sizeof(*nargs));
-			fprintf(stderr, "{A:%p->%p}", args, nargs);
 			if (nargs == NULL
 			    || val_dict_clone(nargs, args) < 0) {
 
@@ -306,7 +321,12 @@
 					free(nargs);
 					p->callstack[i].arguments = NULL;
 				}
-				goto fail2;
+
+				/* Pretend that this round went well,
+				 * so that release1 frees I-th
+				 * fetch_context.  */
+				++i;
+				goto release1;
 			}
 			retp->callstack[i].arguments = nargs;
 		}
diff --git a/proc.h b/proc.h
index f62f5f9..f530e5d 100644
--- a/proc.h
+++ b/proc.h
@@ -64,6 +64,11 @@
 	STATE_IGNORED  /* ignore this process (it's a fork and no -f was used) */
 };
 
+struct output_state {
+	size_t params_left;
+	int need_delim;
+};
+
 struct callstack_element {
 	union {
 		int syscall;
@@ -72,7 +77,9 @@
 	int is_syscall;
 	void * return_addr;
 	struct timeval time_spent;
+	struct fetch_context *fetch_context;
 	struct value_dict *arguments;
+	struct output_state out;
 };
 
 /* XXX We should get rid of this.  */