11#include " tracepointformat.h"
22
3+ #include < QLoggingCategory>
4+
5+ extern " C" {
6+ #include < fmt_parser.h>
7+ #include < fmt_util.h>
8+ }
9+
10+ namespace {
11+ Q_LOGGING_CATEGORY (FormatParser, " hotspot.formatparser" );
12+
13+ auto formatUnsignedNumber (const FormatConversion& format, int base, const QVariant& value)
14+ {
15+ switch (format.len ) {
16+ case FormatConversion::Length::Char:
17+ return QStringLiteral (" %1" ).arg (static_cast <unsigned char >(value.toULongLong ()), format.width , base,
18+ QLatin1Char (' 0' ));
19+ case FormatConversion::Length::Short:
20+ return QStringLiteral (" %1" ).arg (static_cast <unsigned short >(value.toULongLong ()), format.width , base,
21+ QLatin1Char (' 0' ));
22+ case FormatConversion::Length::Long:
23+ return QStringLiteral (" %1" ).arg (static_cast <unsigned int >(value.toULongLong ()), format.width , base,
24+ QLatin1Char (' 0' ));
25+ case FormatConversion::Length::Size:
26+ case FormatConversion::Length::LongLong:
27+ return QStringLiteral (" %1" ).arg (value.toULongLong (), format.width , base, QLatin1Char (' 0' ));
28+ }
29+ Q_UNREACHABLE ();
30+ }
31+
32+ auto formatSignedNumber (const FormatConversion& format, int base, const QVariant& value)
33+ {
34+ switch (format.len ) {
35+ case FormatConversion::Length::Char:
36+ return QStringLiteral (" %1" ).arg (static_cast <char >(value.toLongLong ()), format.width , base, QLatin1Char (' 0' ));
37+ case FormatConversion::Length::Short:
38+ return QStringLiteral (" %1" ).arg (static_cast <short >(value.toLongLong ()), format.width , base, QLatin1Char (' 0' ));
39+ case FormatConversion::Length::Long:
40+ return QStringLiteral (" %1" ).arg (static_cast <int >(value.toLongLong ()), format.width , base, QLatin1Char (' 0' ));
41+ case FormatConversion::Length::Size:
42+ case FormatConversion::Length::LongLong:
43+ return QStringLiteral (" %1" ).arg (value.toLongLong (), format.width , base, QLatin1Char (' 0' ));
44+ }
45+ Q_UNREACHABLE ();
46+ }
47+ }
48+
49+ FormatData parseFormatString (const QString& format)
50+ {
51+ // try to parse the format string
52+ // if it fails or we encounter unknown flags bail out
53+ auto latin = format.toLatin1 ().toStdString ();
54+
55+ const char * str = latin.c_str ();
56+
57+ fmt_status rc;
58+ fmt_spec spec;
59+
60+ QVector<FormatConversion> formats;
61+ QVector<QByteArray> qtFormatString;
62+ int formatCounter = 1 ;
63+
64+ do {
65+ fmt_spec_init (&spec);
66+ rc = fmt_read_one (&str, &spec);
67+ if (rc == FMT_EOK) {
68+ fmt_spec_print (&spec, stdout);
69+ printf (" \n " );
70+ FormatConversion format;
71+
72+ if (spec.kind == FMT_SPEC_KIND_STRING) {
73+ qtFormatString.append (QByteArray {spec.str_start , static_cast <int >(spec.str_end - spec.str_start )});
74+ } else {
75+ qtFormatString.append (QStringLiteral (" %%1" ).arg (formatCounter++).toLatin1 ());
76+
77+ switch (static_cast <fmt_spec_len>(spec.len )) {
78+ case FMT_SPEC_LEN_hh:
79+ format.len = FormatConversion::Length::Char;
80+ break ;
81+ case FMT_SPEC_LEN_h:
82+ format.len = FormatConversion::Length::Short;
83+ break ;
84+ case FMT_SPEC_LEN_L:
85+ case FMT_SPEC_LEN_l:
86+ format.len = FormatConversion::Length::Long;
87+ break ;
88+ case FMT_SPEC_LEN_ll:
89+ format.len = FormatConversion::Length::LongLong;
90+ break ;
91+ case FMT_SPEC_LEN_z:
92+ format.len = FormatConversion::Length::Size;
93+ break ;
94+ case FMT_SPEC_LEN_UNKNOWN:
95+ // no length given
96+ break ;
97+ default :
98+ qCWarning (FormatParser) << " Failed to parse fmt_spec_len" << spec.len ;
99+ return {};
100+ }
101+
102+ switch (static_cast <fmt_spec_type>(spec.type )) {
103+ case FMT_SPEC_TYPE_X:
104+ format.format = FormatConversion::Format::UpperHex;
105+ break ;
106+ case FMT_SPEC_TYPE_x:
107+ format.format = FormatConversion::Format::Hex;
108+ break ;
109+ case FMT_SPEC_TYPE_o:
110+ format.format = FormatConversion::Format::Octal;
111+ break ;
112+ case FMT_SPEC_TYPE_d:
113+ case FMT_SPEC_TYPE_i:
114+ format.format = FormatConversion::Format::Signed;
115+ break ;
116+ case FMT_SPEC_TYPE_u:
117+ format.format = FormatConversion::Format::Unsigned;
118+ break ;
119+ case FMT_SPEC_TYPE_c:
120+ format.format = FormatConversion::Format::Char;
121+ break ;
122+ case FMT_SPEC_TYPE_p:
123+ format.format = FormatConversion::Format::Pointer;
124+ break ;
125+ case FMT_SPEC_TYPE_s:
126+ format.format = FormatConversion::Format::String;
127+ break ;
128+ default :
129+ qCWarning (FormatParser) << " Failed to parse fmt_spec_type" << spec.type ;
130+ return {};
131+ }
132+
133+ if (spec.flags .prepend_zero ) {
134+ format.padZeros = true ;
135+ format.width = spec.width ;
136+
137+ if (spec.width == FMT_VALUE_OUT_OF_LINE) {
138+ return {};
139+ }
140+ }
141+
142+ formats.push_back (format);
143+ }
144+ }
145+ } while (fmt_read_is_ok (rc));
146+
147+ return {formats, QString::fromLatin1 (qtFormatString.join ())};
148+ }
149+
150+ QString format (const FormatConversion& format, const QVariant& value)
151+ {
152+ switch (format.format ) {
153+ case FormatConversion::Format::Signed:
154+ return formatSignedNumber (format, 10 , value);
155+ case FormatConversion::Format::Unsigned:
156+ return formatUnsignedNumber (format, 10 , value);
157+ case FormatConversion::Format::Char:
158+ return value.toChar ();
159+ case FormatConversion::Format::String:
160+ return value.toString ();
161+ case FormatConversion::Format::Pointer:
162+ return QStringLiteral (" 0x%1" ).arg (QString::number (value.toULongLong (), 16 ));
163+ case FormatConversion::Format::Hex:
164+ return formatUnsignedNumber (format, 16 , value);
165+ case FormatConversion::Format::UpperHex:
166+ return formatUnsignedNumber (format, 16 , value).toUpper ();
167+ case FormatConversion::Format::Octal:
168+ return formatUnsignedNumber (format, 8 , value);
169+ }
170+ return {};
171+ }
172+
3173TracePointFormatter::TracePointFormatter (const QString& format)
4174{
5175 // ignore empty format strings
@@ -11,36 +181,31 @@ TracePointFormatter::TracePointFormatter(const QString& format)
11181 // follows a list of arguments
12182 auto endOfFormatString = format.indexOf (QLatin1Char (' \" ' ), 1 );
13183
14- auto lastRec = endOfFormatString;
15- auto recIndex = lastRec;
16-
17- // no quote in format string -> format string is not a string
18- if (endOfFormatString == -1 ) {
19- return ;
20- }
21-
22- // check for valid format string
23- // some format strings contains this tries to filter these out
24- for (int i = endOfFormatString; i < format.size (); i++) {
25- auto c = format[i];
26- auto nextC = i < format.size () - 1 ? format[i + 1 ] : QChar {};
27-
28- if ((c == QLatin1Char (' >' ) && nextC == QLatin1Char (' >' ))
29- || (c == QLatin1Char (' <' ) && nextC == QLatin1Char (' <' ))) {
30- return ;
184+ auto formatStringExtracted = format.mid (1 , endOfFormatString - 1 );
185+ auto formats = parseFormatString (formatStringExtracted);
186+
187+ const auto args = format.mid (endOfFormatString + 2 ).split (QLatin1Char (' ,' ));
188+
189+ // we successfully parsed the string
190+ if (formats.format .size () == args.size ()) {
191+ m_formatString = formats.formatString ;
192+ for (int i = 0 ; i < formats.format .size (); i++) {
193+ const auto & arg = args[i];
194+ auto rec = arg.indexOf (QLatin1String (" REC->" ));
195+
196+ if (rec == -1 ) {
197+ return ;
198+ }
199+
200+ auto closingBracket = arg.indexOf (QLatin1Char (' )' ), rec);
201+ if (closingBracket == -1 ) {
202+ return ;
203+ }
204+ // TODO: safeguard this
205+ rec += 5 ;
206+ m_args.push_back ({formats.format [i], arg.mid (rec, closingBracket - rec)});
31207 }
32208 }
33-
34- // set format string after validating we can print it
35- m_formatString = format.mid (1 , endOfFormatString - 1 );
36-
37- while ((recIndex = format.indexOf (QStringLiteral (" REC->" ), lastRec)) != -1 ) {
38- auto endOfName = format.indexOf (QLatin1Char (' )' ), recIndex);
39-
40- auto start = recIndex + 5 ; // 5 because we want the field after REC->
41- m_args.push_back (format.mid (start, endOfName - start));
42- lastRec = recIndex + 1 ;
43- }
44209}
45210
46211QString TracePointFormatter::format (const Data::TracePointData& data) const
@@ -55,29 +220,22 @@ QString TracePointFormatter::format(const Data::TracePointData& data) const
55220 return result.trimmed ();
56221 }
57222
58- const auto percent = QLatin1Char (' %' );
59- auto currentPercent = 0 ;
60-
61- for (int i = 0 ; i < m_args.size ();) {
62- auto nextPercent = m_formatString.indexOf (percent, currentPercent + 1 );
63-
64- auto substring = m_formatString.mid (currentPercent, nextPercent - currentPercent);
65- if (substring.contains (percent)) {
66- result += QString::asprintf (qPrintable (substring), data.value (m_args[i]).toULongLong ());
67- i++;
68-
69- currentPercent = nextPercent;
70- } else {
71- result += substring;
72- }
73- currentPercent = nextPercent;
223+ result = m_formatString;
224+ for (const auto & arg : m_args) {
225+ result = result.arg (::format (arg.format , data.value (arg.name )));
74226 }
75-
76227 return result;
77228}
78229
79230QString formatTracepoint (const Data::TracePointFormat& format, const Data::TracePointData& data)
80231{
81- TracePointFormatter formatter (format.format );
82- return QStringLiteral (" %1:%2:\n %3" ).arg (format.systemId , format.nameId , formatter.format (data));
232+ static QHash<QString, TracePointFormatter> formatterCache;
233+
234+ const auto name = QStringLiteral (" %1:%2" ).arg (format.systemId , format.nameId );
235+ auto formatter = formatterCache.find (name);
236+ if (formatter == formatterCache.end ()) {
237+ formatter = formatterCache.emplace (name, format.format );
238+ }
239+
240+ return QStringLiteral (" %1:\n %2" ).arg (name, formatter->format (data));
83241}
0 commit comments