+2007-08-16 Josh Stone <joshua.i.stone@intel.com>
+
+ PR 4591
+ * parse.cxx (parser::parse_symbol): Tweak 'print' matching to allow
+ all the new variants with printd and println.
+ * staptree.h (struct print_format): Add fields for the new print
+ variants, and parse_print() to help matching.
+ * staptree.cxx (print_format::parse_print): New static method to
+ match the print variants and determine their properties.
+ (print_format::print): Handle the new print types.
+ (deep_copy_visitor::visit_print_format): Copy the new fields.
+ * translate.cxx (c_unparser::visit_print_format): Insert delims and
+ newlines where appropriate for new print functions.
+ * stap1.in: Document the new print functions.
+
2007-08-15 David Smith <dsmith@redhat.com>
* systemtap.spec.in: Updated %pre to latest Fedora group creation
// now scrutinize this identifier for the various magic forms of identifier
// (printf, @stat_op, and $var...)
+ bool pf_stream, pf_format, pf_delim, pf_newline;
+
if (name.size() > 0 && name[0] == '@')
{
stat_op *sop = new stat_op;
return sop;
}
- else if (name.size() > 0 && (name == "print"
- || name == "sprint"
- || name == "printf"
- || name == "sprintf"))
+ else if (print_format::parse_print(name,
+ pf_stream, pf_format, pf_delim, pf_newline))
{
print_format *fmt = new print_format;
fmt->tok = t;
- fmt->print_with_format = (name[name.size() - 1] == 'f');
- fmt->print_to_stream = (name[0] == 'p');
+ fmt->print_to_stream = pf_stream;
+ fmt->print_with_format = pf_format;
+ fmt->print_with_delim = pf_delim;
+ fmt->print_with_newline = pf_newline;
expect_op("(");
- if (fmt->print_with_format)
- {
- // Consume and convert a format string, and any subsequent
- // arguments. Agreement between the format string and the
- // arguments is postponed to the typechecking phase.
- string tmp;
- expect_unknown (tok_string, tmp);
- fmt->raw_components = tmp;
- fmt->components = print_format::string_to_components (tmp);
- while (!peek_op (")"))
- {
- expect_op(",");
- expression *e = parse_expression ();
- fmt->args.push_back(e);
- }
- }
- else if (name == "print" &&
- (peek_kw("@hist_linear") ||
- peek_kw("@hist_log")))
+ if (name == "print" &&
+ (peek_kw("@hist_linear") || peek_kw("@hist_log")))
{
// We have a special case where we recognize
// print(@hist_foo(bar)) as a magic print-the-histogram
}
else
{
- // If we are not printing with a format string, we permit
- // exactly one argument (of any type).
- expression *e = parse_expression ();
- fmt->args.push_back(e);
+ int min_args = 0;
+ if (fmt->print_with_format)
+ {
+ // Consume and convert a format string. Agreement between the
+ // format string and the arguments is postponed to the
+ // typechecking phase.
+ string tmp;
+ expect_unknown (tok_string, tmp);
+ fmt->raw_components = tmp;
+ fmt->components = print_format::string_to_components (tmp);
+ }
+ else if (fmt->print_with_delim)
+ {
+ // Consume a delimiter to separate arguments.
+ fmt->delimiter.clear();
+ fmt->delimiter.type = print_format::conv_literal;
+ expect_unknown (tok_string, fmt->delimiter.literal_string);
+ min_args = 2;
+ }
+ else
+ {
+ // If we are not printing with a format string, we must have
+ // at least one argument (of any type).
+ expression *e = parse_expression ();
+ fmt->args.push_back(e);
+ }
+
+ // Consume any subsequent arguments.
+ while (min_args || !peek_op (")"))
+ {
+ expect_op(",");
+ expression *e = parse_expression ();
+ fmt->args.push_back(e);
+ if (min_args)
+ --min_args;
+ }
}
expect_op(")");
return fmt;
a macro in the translated C code and is in the neighbourhood of 10.
.SS PRINTING
-The function names
-.IR print ", " printf ", " sprint ", and " sprintf
-are specially treated by the translator. They format values for
-printing to the standard systemtap log stream in a more convenient
-way.
-.PP
+There are a set of function names that are specially treated by the
+translator. They format values for printing to the standard systemtap
+log stream in a more convenient way. The
+.IR sprint*
+variants return the formatted string instead of logging it.
+.TP
+.BR print ", " sprint
+Print one or more values of any type, concatenated directly together.
.TP
-print
-takes a single value of any type, and prints it
+.BR println ", " sprintln
+Print values like
+.IR print " and " sprint ,
+but also append a newline.
.TP
-sprint
-operates like
-.IR print ,
-but returns the formatted string instead of logging it.
+.BR printd ", " sprintd
+Take a string delimiter and two or more values of any type, and print the
+values with the delimiter interposed.
.TP
-printf
-takes a formatting string, and a number of values of corresponding types,
-and prints them all.
+.BR printdln ", " sprintdln
+Print values with a delimiter like
+.IR printd " and " sprintd ,
+but also append a newline.
.TP
-sprintf
-operates like
-.IR printf ,
-but like
-.IR sprint ,
-returns the formatted string instead of logging it.
+.BR printf ", " sprintf
+Take a formatting string and a number of values of corresponding types,
+and print them all.
.PP
The
.IR printf
sprintf("%s before %s",
sprint(1), sprint(3)),
sprint("C"))
+ id[bob] = 1234
+ id[alice] = 5678
+ foreach (name in id)
+ printdln("|", strlen(name), name, id[name])
.ESAMPLE
.SS STATISTICS
}
+bool
+print_format::parse_print(const std::string &name,
+ bool &stream, bool &format, bool &delim, bool &newline)
+{
+ const char *n = name.c_str();
+
+ stream = true;
+ format = delim = newline = false;
+
+ if (*n == 's')
+ {
+ stream = false;
+ ++n;
+ }
+
+ if (0 != strncmp(n, "print", 5))
+ return false;
+ n += 5;
+
+ if (*n == 'f')
+ {
+ format = true;
+ ++n;
+ }
+ else
+ {
+ if (*n == 'd')
+ {
+ delim = true;
+ ++n;
+ }
+
+ if (*n == 'l' && *(n+1) == 'n')
+ {
+ newline = true;
+ n += 2;
+ }
+ }
+
+ return (*n == '\0');
+}
+
+
string
print_format::components_to_string(vector<format_component> const & components)
{
void print_format::print (ostream& o) const
{
- string name = (string(print_to_stream ? "" : "s")
- + string("print")
- + string(print_with_format ? "f" : ""));
- o << name << "(";
+ o << tok->content << "(";
if (print_with_format)
- {
- o << lex_cast_qstring (raw_components);
- }
+ o << lex_cast_qstring (raw_components);
+ if (print_with_delim)
+ o << lex_cast_qstring (delimiter.literal_string);
if (hist)
hist->print(o);
for (vector<expression*>::const_iterator i = args.begin();
i != args.end(); ++i)
{
- if (i != args.begin() || print_with_format)
+ if (i != args.begin() || print_with_format || print_with_delim)
o << ", ";
(*i)->print(o);
}
{
print_format* n = new print_format;
n->tok = e->tok;
- n->print_with_format = e->print_with_format;
n->print_to_stream = e->print_to_stream;
+ n->print_with_format = e->print_with_format;
+ n->print_with_delim = e->print_with_delim;
+ n->print_with_newline = e->print_with_newline;
n->raw_components = e->raw_components;
n->components = e->components;
+ n->delimiter = e->delimiter;
for (unsigned i = 0; i < e->args.size(); ++i)
{
expression* na;
struct print_format: public expression
{
- bool print_with_format;
bool print_to_stream;
+ bool print_with_format;
+ bool print_with_delim;
+ bool print_with_newline;
enum format_flag
{
std::string raw_components;
std::vector<format_component> components;
+ format_component delimiter;
std::vector<expression*> args;
hist_op *hist;
static std::string components_to_string(std::vector<format_component> const & components);
static std::vector<format_component> string_to_components(std::string const & str);
+ static bool parse_print(const std::string &name,
+ bool &stream, bool &format, bool &delim, bool &newline);
void print (std::ostream& o) const;
void visit (visitor* u);
+2007-08-16 Josh Stone <joshua.i.stone@intel.com>
+
+ PR 4591
+ * lib/stap_run.exp: Make sure to match the entire output, in case
+ there are multiple pass/fail messages.
+ * buildok/printf.stp: Add lines for new print variants.
+ * parseko/printd01.stp: Make sure that bad printd calls are handled.
+ * parseko/printd02.stp: Ditto.
+ * parseko/printd03.stp: Ditto.
+ * parseko/printd04.stp: Ditto.
+ * systemtap.base/print.stp: Try a bunch of different print calls.
+ * systemtap.base/print.exp: Driver for above.
+
2007-08-15 Martin Hunt <hunt@redhat.com>
* systemtap.printf/*b.exp: Use stap_merge.tcl.
sprintf("%s before %s",
sprint(1), sprint(3)),
sprint("C"))
- printf("\"quote\\this\"\n")
- printf("%d is %03o in octal\n", 9, 9)
- printf("%d is %#X in hex\n", 255, 255)
- printf("print unsigned %u\n", 17)
- printf("-% d is % d\n", 9, -9)
+ printf("\"quote\\this\"\n")
+ printf("%d is %03o in octal\n", 9, 9)
+ printf("%d is %#X in hex\n", 255, 255)
+ printf("print unsigned %u\n", 17)
+ printf("-% d is % d\n", 9, -9)
+
+ print(1, "two", 3, "four")
+ print(sprint(1, "two", 3, "four"))
+ println(1, "two", 3, "four")
+ print(sprintln(1, "two", 3, "four"))
+
+ printd(", ", 1, "two", 3, "four")
+ print(sprintd(", ", 1, "two", 3, "four"))
+ printdln(", ", 1, "two", 3, "four")
+ print(sprintdln(", ", 1, "two", 3, "four"))
}
}
# tests better all be true
-set all_pass_string "(systemtap test success\r\n)+"
+set all_pass_string "(systemtap test success\r\n)+$"
--- /dev/null
+#! stap -p1
+
+probe begin {
+ // missing the delimiter
+ printd(1, 2, 3, 4, 5)
+}
--- /dev/null
+#! stap -p1
+
+probe begin {
+ // missing the delimiter
+ printdln(1, 2, 3, 4, 5)
+}
--- /dev/null
+#! stap -p1
+
+probe begin {
+ // need more than one value to join
+ printd(", ", 1)
+}
--- /dev/null
+#! stap -p1
+
+probe begin {
+ // need more than one value to join
+ printdln(", ", 1)
+}
--- /dev/null
+# Test that all the print statements work
+
+set test "print"
+
+stap_run $srcdir/$subdir/$test.stp no_load $all_pass_string
--- /dev/null
+/*
+ * print.stp
+ *
+ * Test that all the print statements work
+ */
+
+global s1, s2, s3
+
+probe begin
+{
+ log("systemtap starting probe")
+ s1 = "systemtap"
+ s2 = "test"
+ s3 = "success"
+}
+
+probe end
+{
+ log("systemtap ending probe")
+
+ print(s1, " ", s2, " ", s3, "\n")
+ print(sprint(s1, " ", s2, " ", s3, "\n"))
+
+ println(s1, " ", s2, " ", s3)
+ print(sprintln(s1, " ", s2, " ", s3))
+
+ printd(" ", s1, s2, s3 . "\n")
+ print(sprintd(" ", s1, s2, s3 . "\n"))
+
+ printdln(" ", s1, s2, s3)
+ print(sprintdln(" ", s1, s2, s3))
+
+ // check that formatting characters get escaped correctly in the delimiter
+ s = sprintd("%% % \\ \"", 1, 2, 3)
+ if (s == "1%% % \\ \"2%% % \\ \"3")
+ log("systemtap test success")
+ else
+ log("systemtap test failure")
+}
// directive for each argument.
for (unsigned i = 0; i < e->args.size(); ++i)
{
+ if (i > 0 && e->print_with_delim)
+ components.push_back (e->delimiter);
print_format::format_component curr;
curr.clear();
switch (e->args[i]->type)
}
components.push_back (curr);
}
+
+ if (e->print_with_newline)
+ {
+ print_format::format_component curr;
+ curr.clear();
+ curr.type = print_format::conv_literal;
+ curr.literal_string = "\\n";
+ components.push_back (curr);
+ }
}