Commit 9a77fe4e authored by Alessandro Rubini's avatar Alessandro Rubini

Merge branch 'pp_printf'

This patch-set removes the old diag/printf.c and related files, to use
the "official" pp_printf, which is now published separately and
included here in its entirety with not changes from its upstream repo.

Please note that HAS_DIAG and HAS_FULL_DIAG is not supported any more.

While we have no Kconfig support (coming soon), please see ./MAKEALL
to find how to pass verbosity (and thus size) options to pp_printf.

All commits build for all architectures.
parents 019e2f89 1fad6eae
......@@ -14,9 +14,10 @@ build_one () {
}
build_diags () {
build_one "arch \"$ARCH\", ext \"$PROTO_EXT\", no diag"
build_one "arch \"$ARCH\", ext \"$PROTO_EXT\", mini diag" HAS_DIAG=y
build_one "arch \"$ARCH\", ext \"$PROTO_EXT\", full diag" HAS_FULL_DIAG=y
msg="arch \"$ARCH\", ext \"$PROTO_EXT\""
build_one "$msg, printf none" CONFIG_PRINTF_NONE=y
build_one "$msg, printf xint" CONFIG_PRINTF_XINT=y
build_one "$msg, printf full" CONFIG_PRINTF_FULL=y
}
build_ext () {
......
......@@ -25,7 +25,13 @@ CFLAGS += -Wall -O2 -ggdb -Iinclude
# to avoid ifdef as much as possible, I use the kernel trick for OBJ variables
OBJ-y := fsm.o
# include diagnostic objects
# include pp_printf code, by default the "full" version. Please
# set CONFIG_PRINTF_NONE or CONFIG_PRINTF_XINT if needed.
OBJ-y += pp_printf/pp-printf.o
pp_printf/pp-printf.o: $(wildcard pp_printf/*.[ch])
$(MAKE) -C pp_printf pp-printf.o
include diag/Makefile
# Update 2012-07-10
......
# Alessandro Rubini for CERN, 2011 -- public domain
# Diagnostics: we have tree levels: none, full and limited
# This changed in 2013-02, and it's incompatible with what we had
# full: use a complete printf and the diag code
OBJ-$(HAS_FULL_DIAG) += diag/diag-yes.o
ifdef INTERNAL_PRINTF
OBJ-$(HAS_FULL_DIAG) += diag/diag-printf.o diag/printf-full.o
ifdef PPSI_NO_DIAG
OBJ-y += diag/diag-no.o
else
OBJ-y += diag/diag-yes.o
endif
# limited: use a minimal printf (can't coexist with FULL_DIAG)
ifndef HAS_FULL_DIAG
OBJ-$(HAS_DIAG) += diag/diag-yes.o diag/diag-printf.o diag/printf-mini.o
ifdef HAS_DIAG
SAY := $(shell echo "WARNING: ppsi doesn't use \$$HAS_DIAG any more" >&2)
endif
# no diagnostics: the diag-no.c file includes weak aliases to an empty func
OBJ-y += diag/diag-no.o
ifdef HAS_FULL_DIAG
SAY := $(shell echo "WARNING: ppsi doesn't use \$$HAS_FULL_DIAG any more" >&2)
endif
\ No newline at end of file
......@@ -66,22 +66,13 @@ environment:
CROSS_COMPILE prefix, like "arm-linux-", or empty
PROTO_EXT "whiterabbit" or empty
ARCH name of you arch (default: gnu-linux)
HAS_DIAG set to "y" or leave unset
HAS_FULL_DIAG set to "y" or leave unset
PPSI_NO_DIAG if set, prevents diagnostics from being there
WRMODE (spec only) set to "master" for m/s autodetect; set to
"slave" for slave only configuration
NOTE: The default has no diagnostics, so you most likely will want to run
make HAS_FULL_DIAG=y
or just "make" after running
export HAS_FULL_DIAG=y
If none of the variables are specified, the default Makefile compiles
with standard gcc, over gnu-linux architecture, with no protocol
extension and no diagnostic.
extension and full diagnostic conde (relying on pp_printf).
Implementation Details
......@@ -100,8 +91,9 @@ is LGPL-2.1 or later.
All the rest of the package is built as libraries. The link order
of libraries selects which object files are picked up and which are not.
Additionally, "HAS_DIAG" and "HAS_FULL_DIAG" select by Makefile rules
which diagnostic implementation (if any) is part of the diag library.
Additionally, "CONFIG_PRINTF_XINT" or one of the other pp_printf
configurations can be set to override the default (which selects the
most complete printf implementation from the package).
This state-machine source has three undefined symbols: two for
diagnostics and the "pp_state_table". The table is picked by a
......@@ -158,24 +150,17 @@ similar to what you already see for `arch-'.
Diagnostics
===========
Diagnostic functions may call printf(). If you select HAS_DIAG, the
daemon is built with a minimal printf implementation.
HAS_FULL_DIAG Selects a complete printf implementation
HAS_DIAG Selects a smaller printf
-- By default a no-op printf is selected
The complete implementation comes from an older Linux implementation
(we pulled it from U-boot) as the kernel has currently extra stuff like
%-rules for printing IPV4/IPV6 addresses, symbol tables and so on.
The small implementation receives and parses the complete % formats
but only obeys %c and %s; everything else is printed as 32-bit hex
numbers. So you can use "%-10s %3i" and the output will still make some
sense, saving some kilobytes in code. The code is derived from the full
printf by stripping out code, so the same parsing applies for sure.
With no diagnostics, the printf does nothing and only puts() is available.
Diagnostic functions may call pp_printf(). They should not call
printf() because it is not available when building from freestanding
environments. You can use the Kconfig-like environment variables
to choose which pp_printf implementation to build. The default is
the full function and should be good for everyone. If you have
size problems, please check pp_printf/README about the options.
ppsi up to 2013-01 had no diagnostics by default and used the
environment variables HAS_DIAG and HAS_FULL_DIAG. They are not
used any more and the Makefile warns about them, to ease previous
users.
Command Line
============
......
......@@ -4,6 +4,13 @@
*/
#include <ppsi/lib.h>
/* Architectures declare pp_puts: map puts (used by pp_printf) to it */
int puts(const char *s)
{
pp_puts(s);
return 0;
}
size_t strnlen(const char *s, size_t maxlen)
{
int len = 0;
......
*.o
example-printf
\ No newline at end of file
This diff is collapsed.
# Alessandro Rubini for CERN, 2011 -- public domain
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
CFLAGS += -I. -Os -ggdb -Wall
obj-$(CONFIG_PRINTF_FULL) += vsprintf-full.o
obj-$(CONFIG_PRINTF_MINI) += vsprintf-mini.o
obj-$(CONFIG_PRINTF_NONE) += vsprintf-none.o
obj-$(CONFIG_PRINTF_XINT) += vsprintf-xint.o
# set full as a default if nothing is selected
obj-y ?= vsprintf-full.o
obj-y += printf.o
# There is a static variable in pp-printf.c to accumulate stuff
CONFIG_PRINT_BUFSIZE ?= 256
CFLAGS += -DCONFIG_PRINT_BUFSIZE=$(CONFIG_PRINT_BUFSIZE)
# Targets. You may want to make them different in your package
all: pp-printf.o example-printf
pp-printf.o: $(obj-y)
$(LD) -r $(obj-y) -o $@
example-printf: example-printf.c pp-printf.o
$(CC) $(CFLAGS) $^ -o $@
.c.o:
$(CC) -c $(CFLAGS) $< -o $@
clean:
rm -f *.o *~ example-printf
\ No newline at end of file
This is the "poor programmer's" printf implementation. It is meant to
be used in small environments, like microcontrollers or soft
processors. Actually, that's where I needed it years ago and where
I'm still using it.
It is a complete printf (it only misses the %[charset] feature, and
obviously floating point support). It relies on an external "puts"
function for the actual output; the full version also needs strnlen.
Unfortunately, the stdio puts adds a trailing newline (while most
embedded implementations do not). The sprintf function is included
as pp_sprintf.
The printf engine, vsprintf(), comes in four flavours:
In summary:
- the "full" version is a normal printf (GPL2, from older Linux kernel)
- the "xint" version accepts all formats and prints hex and int only
- the "mini" version accepts all formats but only prints hex (GPL2)
- the "none" version accepts all formats and prints nothing (PD)
The version you use can be selected at compile time, so you can
develop with a full-featured printf and install the minimal one in
production, saving a few kilobytes and still not loosing information
in messages. At the end I list compiled sizes for a few use cases.
While I use this very code in several projects, the only example here
is a stupid main that prints something. You are expected to pick these
files and copy them to your projects, rather than use this "package"
as a system library.
The full implementation in detail
=================================
This comes from u-boot, which means that it is an earlier printf as
used in the Linux kernel. It is licensed according to the GNU GPL
version 2. It includes all formats and prefixes and so on. It is
clearly bugless because everybody is using it.
It is selected at compile time by setting the make variable
"CONFIG_PRINTF_FULL" to "y". You can do that in the environment,
or use Kconfig in your application.
(The Makefile selects this by default if you set nothing in the
environment or make variables)
Example calls (example-printf.c):
pp_printf("integer %5i %5i %05i\n", 1024, 666, 53);
pp_printf("octal %5o %5o %05o\n", 1024, 666, 53);
pp_printf("hex %5x %5x %05x\n", 1024, 666, 53);
pp_printf("HEX etc %5X %+5d %-5i\n", 1024, 666, 53);
pp_printf("neg %5i %05i %05x\n", -5, -10, -15);
pp_printf("char: %c string %s %5s %.5s\n", 65, "foo", "foo",
"verylongstring");
pp_printf("hour %02d:%02d:%02d\n", 12, 9, 0);
Result (as you see, format modifiers are respected):
integer 1024 666 00053
octal 2000 1232 00065
hex 400 29a 00035
HEX etc 400 +666 53
neg -5 -0010 fffffff1
char: A string foo foo veryl
hour 12:09:00
Footprint: 1400-3200 bytes, plus 100-400 bytes for the frontend.
The xint implementation in detail
================================
This prints correctly "%c", "%s", "%i", "%x". Formats "%u" and "%d"
are synonyms of "%i", and "%p" is a synonym for "%x". The only
supported attributes are '0' and a one-digit width (e.g.: "%08x"
works). I personally use it a lot but I don't like it much, because it
is not powerful enough nor low-level as real hacker's too should be.
However, it matches the requirement of some projects with a little
user interface, where the "full" code reveals too large and the "mini"
code is too unfair to the reader. To compile it and link the example,
please set "CONFIG_PRINTF_XINT=y" in your environment or Makefile.
This is the result of the example. As expected, data is aligned and
has leading zeroes when requested, but bot other formats are obeyed:
integer 1024 666 00053
octal 2000 1232 00065
hex 400 29a 00035
HEX etc 400 666 53
neg -5 -0010 fffffff1
char: A string foo foo verylongstring
hour 12:09:00
Footprint: 350-800 bytes, plus 100-400 bytes for the frontend
The miminal implementation in detail
===================================
It is derived from the full one. I left all format parsing intact, but
only print "%s" and "%c". Everything else is printed as hex numbers
like "<badc0ffe>". This means your 47 printed as "%03i" will be output
as "<0000002f>" instead of "047". Still, the standard format is
accepted without errors and no information is lost.
I have made no checks nor reasoning about 32-bit vs 64-bit. I only
used it on 32-bit computers and never printed "long long". Now that it
is published, I have an incentive to do it, though.
It is selected at compile time by setting CONFIG_PRINTF_MINI=y as a
make variable, possibly inherited by the environment. It is licensed
as GPL version 2 because it's derived from the full one -- I left the
parsing as I found in there.
Result of example-printf (you can "make CONFIG_PRINTF_MINI=y):
integer <00000400> <0000029a> <00000035>
octal <00000400> <0000029a> <00000035>
hex <00000400> <0000029a> <00000035>
HEX etc <00000400> <0000029a> <00000035>
neg <fffffffb> <fffffff6> <fffffff1>
char: A string foo foo verylongstring
hour <0000000c>:<00000009>:<00000000>
As promised, %c and %s is printed correctly, but without obeying the
format modifiers, but all integer value are printed in hex.
Footprint: 200-600 bytes, plus 100-400 for the frontend.
The empty implementation in detail
==================================
The empty implementation, called "none" to respect the 4-letter
pattern of "full" and "mini" doesn't parse any format. It simply
prints the format string and nothing more. This allows to keep the
most important messages, like the welcome string or a "Panic" string,
while saving code space.
It is selected at compile time by setting CONFIG_PRINTF_NONE.
Result of example-printf (you can "make CONFIG_PRINTF_MINI=y):
integer %5i %5i %05i
octal %5o %5o %05o
hex %5x %5x %05x
HEX etc %5X %+5d %-5i
neg %5i %05i %05x
char: %c string %s %5s %.5s
hour %02d:%02d:%02d
Footprint: 25-110 bytes, plus 100-400 for the frontend.
If you want to remove all printf overhead in production, you should
use a preprocessor macro to completely kill the printf calls. This
would save you the parameter-passing overhead in the caller and all
the constant strings in .rodata. I don't support this in the package,
though, and I discourage from doing it, for the usual
preprocessor-related reasons.
Footprint of the various implementations
========================================
This table excludes the static buffer (256 in .bss by default) and
only lists the code size (command "size", column "text"), compiled
with -Os as for this Makefile.
printf.o is the frontend and is linked in all four configurations,
the other ones are exclusive one another:
printf.o full xint mini none
x86, gcc-4.4.5 87 1715 476 258 48
x86-64, gcc-4.4.5 418 2325 712 433 77
x86, gcc-4.6.2 255 2210 577 330 110
arm, gcc-4.2.2 156 2408 684 356 52
arm, gcc-4.5.2 128 2235 645 353 44
arm, gcc-4.5.2 thumb2 80 1443 373 209 26
lm32, gcc-4.5.3 196 3228 792 576 44
mips, gcc-4.4.1 184 2616 824 504 72
powerpc, gcc-4.4.1 328 2895 881 521 48
coldfire, gcc-4.4.1 96 2025 485 257 42
sh4, gcc-4.4.1 316 2152 608 408 34
#include <stdio.h>
#include <pp-printf.h>
int main(int argc, char **argv)
{
pp_printf("integer %5i %5i %05i\n", 1024, 666, 53);
pp_printf("octal %5o %5o %05o\n", 1024, 666, 53);
pp_printf("hex %5x %5x %05x\n", 1024, 666, 53);
pp_printf("HEX etc %5X %+5d %-5i\n", 1024, 666, 53);
pp_printf("neg %5i %05i %05x\n", -5, -10, -15);
pp_printf("char: %c string %s %5s %.5s\n", 65, "foo", "foo",
"verylongstring");
pp_printf("hour %02d:%02d:%02d\n", 12, 9, 0);
return 0;
}
#include <stdarg.h>
extern int pp_printf(const char *fmt, ...)
__attribute__((format(printf,1,2)));
extern int pp_sprintf(char *s, const char *fmt, ...)
__attribute__((format(printf,2,3)));
extern int pp_vprintf(const char *fmt, va_list args);
extern int pp_vsprintf(char *buf, const char *, va_list)
__attribute__ ((format (printf, 2, 0)));
/* This is what we rely on for output */
extern int puts(const char *s);
......@@ -5,30 +5,39 @@
* (please note that the vsprintf is not public domain but GPL)
*/
#include <stdarg.h>
#include <ppsi/ppsi.h>
#include <ppsi/diag.h>
#include <pp-printf.h>
#define PP_BUF 128 /* We prefer small targets */
static char print_buf[PP_BUF];
static char print_buf[CONFIG_PRINT_BUFSIZE];
int pp_vprintf(const char *fmt, va_list args)
{
int ret;
ret = pp_vsprintf(print_buf, fmt, args);
pp_puts(print_buf);
puts(print_buf);
return ret;
}
int pp_sprintf(char *s, const char *fmt, ...)
{
va_list args;
int ret;
va_start(args, fmt);
ret = pp_vsprintf(s, fmt, args);
va_end(args);
return ret;
}
int pp_printf(const char *fmt, ...)
{
va_list args;
int r;
int ret;
va_start(args, fmt);
r = pp_vprintf(fmt, args);
ret = pp_vprintf(fmt, args);
va_end(args);
return r;
return ret;
}
......@@ -12,9 +12,11 @@
/* Retrieved from u-boot on 2010-02, changed some stuff (ARub) */
#include <stdarg.h>
#include <stdint.h>
#include <string.h>
/* BEGIN OF HACKS */
#include <ppsi/ppsi.h>
#include <pp-printf.h>
/* <ctype.h> */
static inline int isdigit(int c)
......
#include <stdarg.h>
#include <pp-printf.h>
/*
* empty vsprintf: only the format string. Public domain
*/
int pp_vsprintf(char *buf, const char *fmt, va_list args)
{
char *str = buf;
for (; *fmt ; ++fmt)
*str++ = *fmt;
*str++ = '\0';
return str - buf;
}
/*
* vsprintf-xint: a possible free-software replacement for mprintf
*
* public domain
*/
#include <stdarg.h>
#include <stdint.h>
static const char hex[] = "0123456789abcdef";
static int number(char *out, unsigned value, int base, int lead, int wid)
{
char tmp[16];
int i = 16, ret, negative = 0;
/* No error checking at all: it is as ugly as possible */
if ((signed)value < 0 && base == 10) {
negative = 1;
value = -value;
}
while (value && i) {
tmp[--i] = hex[value % base];
value /= base;
}
if (i == 16)
tmp[--i] = '0';
if (negative && lead == ' ') {
tmp[--i] = '-';
negative = 0;
}
while (i > 16 - wid + negative)
tmp[--i] = lead;
if (negative)
tmp[--i] = '-';
ret = 16 - i;
while (i < 16)
*(out++) = tmp[i++];
return ret;
}
int pp_vsprintf(char *buf, const char *fmt, va_list args)
{
char *s, *str = buf;
int base, lead, wid;
for (; *fmt ; ++fmt) {
if (*fmt != '%') {
*str++ = *fmt;
continue;
}
base = 10;
lead = ' ';
wid = 1;
repeat:
fmt++; /* Skip '%' initially, other stuff later */
switch(*fmt) {
case '\0':
goto ret;
case '0':
lead = '0';
goto repeat;
case '*':
/* should be precision, just eat it */
base = va_arg(args, int);
/* fall through: discard unknown stuff */
default:
if (*fmt >= '1' && *fmt <= '9')
wid = *fmt - '0';
goto repeat;
/* Special cases for conversions */
case 'c': /* char: supported */
*str++ = (unsigned char) va_arg(args, int);
break;
case 's': /* string: supported */
s = va_arg(args, char *);
while (*s)
*str++ = *s++;
break;
case 'n': /* number-thus-far: not supported */
break;
case '%': /* supported */
*str++ = '%';
break;
/* integers are more or less printed */
case 'p':
case 'x':
case 'X':
base = 16;
case 'o':
if (base == 10) /* yet unchaged */
base = 8;
case 'd':
case 'i':
case 'u':
str += number(str, va_arg(args, int), base, lead, wid);
break;
}
}
ret:
*str = '\0';
return str - buf;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment