diff mbox

[V8,1/2] printk: remove tabular output for NULL pointer

Message ID 1508986436-31966-2-git-send-email-me@tobin.cc (mailing list archive)
State New, archived
Headers show

Commit Message

Tobin Harding Oct. 26, 2017, 2:53 a.m. UTC
Currently pointer() checks for a NULL pointer argument and then if so
attempts to print "(null)" with _some_ standard width. This width cannot
correctly be ascertained here because many of the printk specifiers
print pointers of varying widths.

Remove the attempt to print NULL pointers with a correct width.

Signed-off-by: Tobin C. Harding <me@tobin.cc>
---
 lib/vsprintf.c | 9 +--------
 1 file changed, 1 insertion(+), 8 deletions(-)

Comments

Joe Perches Oct. 26, 2017, 4:57 a.m. UTC | #1
On Thu, 2017-10-26 at 13:53 +1100, Tobin C. Harding wrote:
> Currently pointer() checks for a NULL pointer argument and then if so
> attempts to print "(null)" with _some_ standard width. This width cannot
> correctly be ascertained here because many of the printk specifiers
> print pointers of varying widths.

I believe this is not a good change.
Only pointers without a <foo> extension call pointer()

> Remove the attempt to print NULL pointers with a correct width.

the correct width for a %p is the default width.
The correct width for %p<foo> is unknown.

> Signed-off-by: Tobin C. Harding <me@tobin.cc>
> ---
>  lib/vsprintf.c | 9 +--------
>  1 file changed, 1 insertion(+), 8 deletions(-)
> 
> diff --git a/lib/vsprintf.c b/lib/vsprintf.c
> index 86c3385b9eb3..16a587aed40e 100644
> --- a/lib/vsprintf.c
> +++ b/lib/vsprintf.c
> @@ -1710,15 +1710,8 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
>  {
>  	const int default_width = 2 * sizeof(void *);
>  
> -	if (!ptr && *fmt != 'K') {
> -		/*
> -		 * Print (null) with the same width as a pointer so it makes
> -		 * tabular output look nice.
> -		 */
> -		if (spec.field_width == -1)
> -			spec.field_width = default_width;
> +	if (!ptr && *fmt != 'K')
>  		return string(buf, end, "(null)", spec);
> -	}
>  
>  	switch (*fmt) {
>  	case 'F':
Tobin Harding Oct. 26, 2017, 6:27 a.m. UTC | #2
Hi Joe,

thanks for your review.

On Wed, Oct 25, 2017 at 09:57:23PM -0700, Joe Perches wrote:
> On Thu, 2017-10-26 at 13:53 +1100, Tobin C. Harding wrote:
> > Currently pointer() checks for a NULL pointer argument and then if so
> > attempts to print "(null)" with _some_ standard width. This width cannot
> > correctly be ascertained here because many of the printk specifiers
> > print pointers of varying widths.
> 
> I believe this is not a good change.
> Only pointers without a <foo> extension call pointer()

Sorry, I don't understand what you mean here. All the %p<foo> specifier code is
handled by pointer()?

> > Remove the attempt to print NULL pointers with a correct width.
> 
> the correct width for a %p is the default width.

It is the default width if we are printing addresses. Once we hash 64
bit address to a 32 bit identifier then we don't have a default width.

> The correct width for %p<foo> is unknown.

I agree.

If I have misunderstood you, please forgive me. I am very appreciative
of the reviews this patch is getting and the patience the list is having
with the many iterations.

thanks,
Tobin.
Joe Perches Oct. 26, 2017, 8:05 a.m. UTC | #3
On Thu, 2017-10-26 at 17:27 +1100, Tobin C. Harding wrote:
> Hi Joe,
> 
> thanks for your review.
> 
> On Wed, Oct 25, 2017 at 09:57:23PM -0700, Joe Perches wrote:
> > On Thu, 2017-10-26 at 13:53 +1100, Tobin C. Harding wrote:
> > > Currently pointer() checks for a NULL pointer argument and then if so
> > > attempts to print "(null)" with _some_ standard width. This width cannot
> > > correctly be ascertained here because many of the printk specifiers
> > > print pointers of varying widths.
> > 
> > I believe this is not a good change.
> > Only pointers without a <foo> extension call pointer()
>
> Sorry, I don't understand what you mean here. All the %p<foo> specifier code is
> handled by pointer()?

Sorry, I was imprecise/wrong.

None of the %p<foo> extensions except %pK and %p<invalid_foo>
actually use this bit of the pointer() call.

All of the other valid %p<foo> extension uses do not end up
at this block being executed so it's effectively only regular
pointers being output by number()

> > > Remove the attempt to print NULL pointers with a correct width.
> > 
> > the correct width for a %p is the default width.
> 
> It is the default width if we are printing addresses. Once we hash 64
> bit address to a 32 bit identifier then we don't have a default width.

Perhaps that 32 bit identifier should use leading 0's for
the default width.

aside:

Why hash 64 bits to 32?
Why shouldn't the hash width be 64 bits on 64 bit systems?
Tobin Harding Oct. 26, 2017, 9:37 a.m. UTC | #4
On Thu, Oct 26, 2017 at 01:05:39AM -0700, Joe Perches wrote:
> On Thu, 2017-10-26 at 17:27 +1100, Tobin C. Harding wrote:
> > Hi Joe,
> > 
> > thanks for your review.
> > 
> > On Wed, Oct 25, 2017 at 09:57:23PM -0700, Joe Perches wrote:
> > > On Thu, 2017-10-26 at 13:53 +1100, Tobin C. Harding wrote:
> > > > Currently pointer() checks for a NULL pointer argument and then if so
> > > > attempts to print "(null)" with _some_ standard width. This width cannot
> > > > correctly be ascertained here because many of the printk specifiers
> > > > print pointers of varying widths.
> > > 
> > > I believe this is not a good change.
> > > Only pointers without a <foo> extension call pointer()
> >
> > Sorry, I don't understand what you mean here. All the %p<foo> specifier code is
> > handled by pointer()?
> 
> Sorry, I was imprecise/wrong.
> 
> None of the %p<foo> extensions except %pK and %p<invalid_foo>
> actually use this bit of the pointer() call.

	if (!ptr && *fmt != 'K') {
		/*
		 * Print (null) with the same width as a pointer so it makes
		 * tabular output look nice.
		 */
		if (spec.field_width == -1)
			spec.field_width = default_width;
		return string(buf, end, "(null)", spec);
	}

Is there something I'm missing here? This code reads like its all %p<foo>
(including %p and %p<invalid_foo>) except %pK that hit this block when
a NULL pointer is passed in.

> All of the other valid %p<foo> extension uses do not end up
> at this block being executed so it's effectively only regular
> pointers being output by number()
> 
> > > > Remove the attempt to print NULL pointers with a correct width.
> > > 
> > > the correct width for a %p is the default width.
> > 
> > It is the default width if we are printing addresses. Once we hash 64
> > bit address to a 32 bit identifier then we don't have a default width.
> 
> Perhaps that 32 bit identifier should use leading 0's for
> the default width.

That's a fair comment.

> aside:
> 
> Why hash 64 bits to 32?
> Why shouldn't the hash width be 64 bits on 64 bit systems?

Quoted from Linus in an earlier thread discussing this change

	Date: Thu, 12 Oct 2017 11:37:22 -0700 Linus Torvalds wrote:

	In fact, I'd prefer mapping the pointer to a 32-bit value, even on
	64-bit architectures. When people use these things for debugging and
	for identifying which device node or socket or whatever they are
	tracking, we're generally talking a (small) handful of different
	devices or whatever.


Hope this helps,
Tobin.
Joe Perches Oct. 26, 2017, 2:47 p.m. UTC | #5
On Thu, 2017-10-26 at 20:37 +1100, Tobin C. Harding wrote:
> On Thu, Oct 26, 2017 at 01:05:39AM -0700, Joe Perches wrote:
> > On Thu, 2017-10-26 at 17:27 +1100, Tobin C. Harding wrote:
> > > Hi Joe,
> > > 
> > > thanks for your review.
> > > 
> > > On Wed, Oct 25, 2017 at 09:57:23PM -0700, Joe Perches wrote:
> > > > On Thu, 2017-10-26 at 13:53 +1100, Tobin C. Harding wrote:
> > > > > Currently pointer() checks for a NULL pointer argument and then if so
> > > > > attempts to print "(null)" with _some_ standard width. This width cannot
> > > > > correctly be ascertained here because many of the printk specifiers
> > > > > print pointers of varying widths.
> > > > 
> > > > I believe this is not a good change.
> > > > Only pointers without a <foo> extension call pointer()
> > > 
> > > Sorry, I don't understand what you mean here. All the %p<foo> specifier code is
> > > handled by pointer()?
> > 
> > Sorry, I was imprecise/wrong.
> > 
> > None of the %p<foo> extensions except %pK and %p<invalid_foo>
> > actually use this bit of the pointer() call.
> 
> 	if (!ptr && *fmt != 'K') {
> 		/*
> 		 * Print (null) with the same width as a pointer so it makes
> 		 * tabular output look nice.
> 		 */
> 		if (spec.field_width == -1)
> 			spec.field_width = default_width;
> 		return string(buf, end, "(null)", spec);
> 	}
> 
> Is there something I'm missing here? This code reads like its all %p<foo>
> (including %p and %p<invalid_foo>) except %pK that hit this block when
> a NULL pointer is passed in.

The idea for aligning is described in commit 5e0579812834a

$ git log --stat -p -1 --format=email 5e0579812834a
From 5e0579812834ab7fa072db4a15ebdff68d62e2e7 Mon Sep 17 00:00:00 2001
From: Joe Perches <joe@perches.com>
Date: Tue, 26 Oct 2010 14:22:50 -0700
Subject: [PATCH] vsprintf.c: use default pointer field size for "(null)"
 strings

It might be nicer to align the output.

For instance, ACPI messages sometimes have "(null)" pointers.

$ dmesg | grep "(null)"  -A 1 -B 1
[    0.198733] ACPI: Dynamic OEM Table Load:
[    0.198745] ACPI: SSDT (null) 00239 (v02  PmRef  Cpu0Ist 00003000 INTL 20051117)
[    0.199294] ACPI: SSDT 7f596e10 001C7 (v02  PmRef  Cpu0Cst 00003001 INTL 20051117)
[    0.200708] ACPI: Dynamic OEM Table Load:
[    0.200721] ACPI: SSDT (null) 001C7 (v02  PmRef  Cpu0Cst 00003001 INTL 20051117)
[    0.201950] ACPI: SSDT 7f597f10 000D0 (v02  PmRef  Cpu1Ist 00003000 INTL 20051117)
[    0.203386] ACPI: Dynamic OEM Table Load:
[    0.203398] ACPI: SSDT (null) 000D0 (v02  PmRef  Cpu1Ist 00003000 INTL 20051117)
[    0.203871] ACPI: SSDT 7f595f10 00083 (v02  PmRef  Cpu1Cst 00003000 INTL 20051117)
[    0.205301] ACPI: Dynamic OEM Table Load:
[    0.205315] ACPI: SSDT (null) 00083 (v02  PmRef  Cpu1Cst 00003000 INTL 20051117)

> > All of the other valid %p<foo> extension uses do not end up
> > at this block being executed so it's effectively only regular
> > pointers being output by number()

Because passing NULL to any of the %p<foo> extensions
excluding %pK is probably a defect.

> > > > > Remove the attempt to print NULL pointers with a correct width.
> > > > 
> > > > the correct width for a %p is the default width.
> > > 
> > > It is the default width if we are printing addresses. Once we hash 64
> > > bit address to a 32 bit identifier then we don't have a default width.
> > 
> > Perhaps that 32 bit identifier should use leading 0's for
> > the default width.
> 
> That's a fair comment.
> 
> > aside:
> > 
> > Why hash 64 bits to 32?
> > Why shouldn't the hash width be 64 bits on 64 bit systems?
> 
> Quoted from Linus in an earlier thread discussing this change
> 
> 	Date: Thu, 12 Oct 2017 11:37:22 -0700 Linus Torvalds wrote:
> 
> 	In fact, I'd prefer mapping the pointer to a 32-bit value, even on
> 	64-bit architectures. When people use these things for debugging and
> 	for identifying which device node or socket or whatever they are
> 	tracking, we're generally talking a (small) handful of different
> 	devices or whatever.

I wonder about this and userland programs and API breakage.

I'd expect there could be cases of userland parsers that
expect a certain width for pointer fields.

$ git grep -E "\bseq_.*%p\W" | wc -l
112
Tobin Harding Oct. 26, 2017, 11:57 p.m. UTC | #6
On Thu, Oct 26, 2017 at 07:47:19AM -0700, Joe Perches wrote:
> On Thu, 2017-10-26 at 20:37 +1100, Tobin C. Harding wrote:
> > On Thu, Oct 26, 2017 at 01:05:39AM -0700, Joe Perches wrote:
> > > On Thu, 2017-10-26 at 17:27 +1100, Tobin C. Harding wrote:
> > > > Hi Joe,
> > > > 
> > > > thanks for your review.
> > > > 
> > > > On Wed, Oct 25, 2017 at 09:57:23PM -0700, Joe Perches wrote:
> > > > > On Thu, 2017-10-26 at 13:53 +1100, Tobin C. Harding wrote:
> > > > > > Currently pointer() checks for a NULL pointer argument and then if so
> > > > > > attempts to print "(null)" with _some_ standard width. This width cannot
> > > > > > correctly be ascertained here because many of the printk specifiers
> > > > > > print pointers of varying widths.
> > > > > 
> > > > > I believe this is not a good change.
> > > > > Only pointers without a <foo> extension call pointer()
> > > > 
> > > > Sorry, I don't understand what you mean here. All the %p<foo> specifier code is
> > > > handled by pointer()?
> > > 
> > > Sorry, I was imprecise/wrong.
> > > 
> > > None of the %p<foo> extensions except %pK and %p<invalid_foo>
> > > actually use this bit of the pointer() call.
> > 
> > 	if (!ptr && *fmt != 'K') {
> > 		/*
> > 		 * Print (null) with the same width as a pointer so it makes
> > 		 * tabular output look nice.
> > 		 */
> > 		if (spec.field_width == -1)
> > 			spec.field_width = default_width;
> > 		return string(buf, end, "(null)", spec);
> > 	}
> > 
> > Is there something I'm missing here? This code reads like its all %p<foo>
> > (including %p and %p<invalid_foo>) except %pK that hit this block when
> > a NULL pointer is passed in.
> 
> The idea for aligning is described in commit 5e0579812834a
> 
> $ git log --stat -p -1 --format=email 5e0579812834a
> From 5e0579812834ab7fa072db4a15ebdff68d62e2e7 Mon Sep 17 00:00:00 2001
> From: Joe Perches <joe@perches.com>
> Date: Tue, 26 Oct 2010 14:22:50 -0700
> Subject: [PATCH] vsprintf.c: use default pointer field size for "(null)"
>  strings
> 
> It might be nicer to align the output.
> 
> For instance, ACPI messages sometimes have "(null)" pointers.
> 
> $ dmesg | grep "(null)"  -A 1 -B 1
> [    0.198733] ACPI: Dynamic OEM Table Load:
> [    0.198745] ACPI: SSDT (null) 00239 (v02  PmRef  Cpu0Ist 00003000 INTL 20051117)
> [    0.199294] ACPI: SSDT 7f596e10 001C7 (v02  PmRef  Cpu0Cst 00003001 INTL 20051117)
> [    0.200708] ACPI: Dynamic OEM Table Load:
> [    0.200721] ACPI: SSDT (null) 001C7 (v02  PmRef  Cpu0Cst 00003001 INTL 20051117)
> [    0.201950] ACPI: SSDT 7f597f10 000D0 (v02  PmRef  Cpu1Ist 00003000 INTL 20051117)
> [    0.203386] ACPI: Dynamic OEM Table Load:
> [    0.203398] ACPI: SSDT (null) 000D0 (v02  PmRef  Cpu1Ist 00003000 INTL 20051117)
> [    0.203871] ACPI: SSDT 7f595f10 00083 (v02  PmRef  Cpu1Cst 00003000 INTL 20051117)
> [    0.205301] ACPI: Dynamic OEM Table Load:
> [    0.205315] ACPI: SSDT (null) 00083 (v02  PmRef  Cpu1Cst 00003000 INTL 20051117)

Does this give the correct level of control? Would it not be better to
control the output of NULL pointers in the code that prints them. The
other side of the coin is that with padding a random single debug
message ends up with unwanted white space, e.g

 [    0.205315] foo: This pointer (null)         some useful error message 

Just thoughts.

> > > All of the other valid %p<foo> extension uses do not end up
> > > at this block being executed so it's effectively only regular
> > > pointers being output by number()
> 
> Because passing NULL to any of the %p<foo> extensions
> excluding %pK is probably a defect.

This implies that passing NULL to %p is a defect also, does it not. Like
already stated, if it is not a defect, the calling code is in a better
position to decide on the formatting, unless we printed
	<-    (null)    ->

with the correct spacing for all architectures, this seems a bit
extravagant though.

> > > > > > Remove the attempt to print NULL pointers with a correct width.
> > > > > 
> > > > > the correct width for a %p is the default width.
> > > > 
> > > > It is the default width if we are printing addresses. Once we hash 64
> > > > bit address to a 32 bit identifier then we don't have a default width.
> > > 
> > > Perhaps that 32 bit identifier should use leading 0's for
> > > the default width.
> > 
> > That's a fair comment.
> > 
> > > aside:
> > > 
> > > Why hash 64 bits to 32?
> > > Why shouldn't the hash width be 64 bits on 64 bit systems?
> > 
> > Quoted from Linus in an earlier thread discussing this change
> > 
> > 	Date: Thu, 12 Oct 2017 11:37:22 -0700 Linus Torvalds wrote:
> > 
> > 	In fact, I'd prefer mapping the pointer to a 32-bit value, even on
> > 	64-bit architectures. When people use these things for debugging and
> > 	for identifying which device node or socket or whatever they are
> > 	tracking, we're generally talking a (small) handful of different
> > 	devices or whatever.
> 
> I wonder about this and userland programs and API breakage.
> 
> I'd expect there could be cases of userland parsers that
> expect a certain width for pointer fields.
> 
> $ git grep -E "\bseq_.*%p\W" | wc -l
> 112

This is a good point. Making %p now prefix with 0x could also
potentially break things for the same reason. Perhaps your suggestion of
having leading 0's in front of the 32 bit identifier on 64 bit machines
solves a number of these problems (without the 0x prefix).

1. It leaves the output layout unchanged, no userland breakages.
2. It still has the advantages of a 32 bit hash mentioned by Linus.
3. It makes explicit that something funny is going on with the address,
   multiple addresses with 32 leading 0's will stand out.


thanks,
Tobin.
Joe Perches Oct. 27, 2017, 12:11 a.m. UTC | #7
On Fri, 2017-10-27 at 10:57 +1100, Tobin C. Harding wrote:
> On Thu, Oct 26, 2017 at 07:47:19AM -0700, Joe Perches wrote:
> > On Thu, 2017-10-26 at 20:37 +1100, Tobin C. Harding wrote:
> > > On Thu, Oct 26, 2017 at 01:05:39AM -0700, Joe Perches wrote:
> > > > On Thu, 2017-10-26 at 17:27 +1100, Tobin C. Harding wrote:
> > > > > Hi Joe,
> > > > > 
> > > > > thanks for your review.
> > > > > 
> > > > > On Wed, Oct 25, 2017 at 09:57:23PM -0700, Joe Perches wrote:
> > > > > > On Thu, 2017-10-26 at 13:53 +1100, Tobin C. Harding wrote:
> > > > > > > Currently pointer() checks for a NULL pointer argument and then if so
> > > > > > > attempts to print "(null)" with _some_ standard width. This width cannot
> > > > > > > correctly be ascertained here because many of the printk specifiers
> > > > > > > print pointers of varying widths.
> > > > > > 
> > > > > > I believe this is not a good change.
> > > > > > Only pointers without a <foo> extension call pointer()
> > > > > 
> > > > > Sorry, I don't understand what you mean here. All the %p<foo> specifier code is
> > > > > handled by pointer()?
> > > > 
> > > > Sorry, I was imprecise/wrong.
> > > > 
> > > > None of the %p<foo> extensions except %pK and %p<invalid_foo>
> > > > actually use this bit of the pointer() call.
> > > 
> > > 	if (!ptr && *fmt != 'K') {
> > > 		/*
> > > 		 * Print (null) with the same width as a pointer so it makes
> > > 		 * tabular output look nice.
> > > 		 */
> > > 		if (spec.field_width == -1)
> > > 			spec.field_width = default_width;
> > > 		return string(buf, end, "(null)", spec);
> > > 	}
> > > 
> > > Is there something I'm missing here? This code reads like its all %p<foo>
> > > (including %p and %p<invalid_foo>) except %pK that hit this block when
> > > a NULL pointer is passed in.
> > 
> > The idea for aligning is described in commit 5e0579812834a
> > 
> > $ git log --stat -p -1 --format=email 5e0579812834a
> > From 5e0579812834ab7fa072db4a15ebdff68d62e2e7 Mon Sep 17 00:00:00 2001
> > From: Joe Perches <joe@perches.com>
> > Date: Tue, 26 Oct 2010 14:22:50 -0700
> > Subject: [PATCH] vsprintf.c: use default pointer field size for "(null)"
> >  strings
> > 
> > It might be nicer to align the output.
> > 
> > For instance, ACPI messages sometimes have "(null)" pointers.
> > 
> > $ dmesg | grep "(null)"  -A 1 -B 1
> > [    0.198733] ACPI: Dynamic OEM Table Load:
> > [    0.198745] ACPI: SSDT (null) 00239 (v02  PmRef  Cpu0Ist 00003000 INTL 20051117)
> > [    0.199294] ACPI: SSDT 7f596e10 001C7 (v02  PmRef  Cpu0Cst 00003001 INTL 20051117)
> > [    0.200708] ACPI: Dynamic OEM Table Load:
> > [    0.200721] ACPI: SSDT (null) 001C7 (v02  PmRef  Cpu0Cst 00003001 INTL 20051117)
> > [    0.201950] ACPI: SSDT 7f597f10 000D0 (v02  PmRef  Cpu1Ist 00003000 INTL 20051117)
> > [    0.203386] ACPI: Dynamic OEM Table Load:
> > [    0.203398] ACPI: SSDT (null) 000D0 (v02  PmRef  Cpu1Ist 00003000 INTL 20051117)
> > [    0.203871] ACPI: SSDT 7f595f10 00083 (v02  PmRef  Cpu1Cst 00003000 INTL 20051117)
> > [    0.205301] ACPI: Dynamic OEM Table Load:
> > [    0.205315] ACPI: SSDT (null) 00083 (v02  PmRef  Cpu1Cst 00003000 INTL 20051117)
> 
> Does this give the correct level of control? Would it not be better to
> control the output of NULL pointers in the code that prints them. The
> other side of the coin is that with padding a random single debug
> message ends up with unwanted white space, e.g
> 
>  [    0.205315] foo: This pointer (null)         some useful error message 
> 
> Just thoughts.

I'm not sure there are any of those uses.
Perhaps you could show actual examples.

> > > > All of the other valid %p<foo> extension uses do not end up
> > > > at this block being executed so it's effectively only regular
> > > > pointers being output by number()
> > 
> > Because passing NULL to any of the %p<foo> extensions
> > excluding %pK is probably a defect.
> 
> This implies that passing NULL to %p is a defect also, does it not.

No, it does not imply that.

%p and %pK just print the value of the pointer arg.
%p<foo> extensions other than %pK dereference the pointer arg.
NULL
dereferences cause an oops.

> > I'd expect there could be cases of userland parsers that
> > expect a certain width for pointer fields.
> > 
> > $ git grep -E "\bseq_.*%p\W" | wc -l
> > 112
> 
> This is a good point. Making %p now prefix with 0x could also
> potentially break things for the same reason.

I thought it was agreed not to do that.

> Perhaps your suggestion of
> having leading 0's in front of the 32 bit identifier on 64 bit machines
> solves a number of these problems (without the 0x prefix).
> 
> 1. It leaves the output layout unchanged, no userland breakages.
> 2. It still has the advantages of a 32 bit hash mentioned by Linus.
> 3. It makes explicit that something funny is going on with the address,
>    multiple addresses with 32 leading 0's will stand out.

cheers, Joe
diff mbox

Patch

diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 86c3385b9eb3..16a587aed40e 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -1710,15 +1710,8 @@  char *pointer(const char *fmt, char *buf, char *end, void *ptr,
 {
 	const int default_width = 2 * sizeof(void *);
 
-	if (!ptr && *fmt != 'K') {
-		/*
-		 * Print (null) with the same width as a pointer so it makes
-		 * tabular output look nice.
-		 */
-		if (spec.field_width == -1)
-			spec.field_width = default_width;
+	if (!ptr && *fmt != 'K')
 		return string(buf, end, "(null)", spec);
-	}
 
 	switch (*fmt) {
 	case 'F':