Skip to content

Commit

Permalink
TracepointSpec - expose a Trim method (#64)
Browse files Browse the repository at this point in the history
TracepointSpec is a class for parsing user-supplied input. The first
thing it does is trim leading/trailing whitespace. It provides access to
several parsed-out chunks of the input, but does not provide access to
this trimmed version of the full input.

It seems likely that callers will want access to this trimmed version of
the user-supplied input, whether for logging or for error reporting. For
example, the tracepoint-collect tool is doing its own trimming.

Provide access to the trimming functionality so callers don't have to
duplicate it.

- Provide a static Trim method that can be used before calling the
  constructor to access the trimmed version. Useful if we need to
  allocate storage for the spec string (has to happen before parsing since
  TracepointSpec is a non-owning view).
- Provide a Trimmed field that stores the trimmed string (useful for
  logging or error messages after calling the constructor).
  • Loading branch information
idigdoug authored Apr 24, 2024
1 parent 22e979a commit e12bc6a
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 64 deletions.
10 changes: 9 additions & 1 deletion libtracepoint-control-cpp/include/tracepoint/TracepointSpec.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ namespace tracepoint_control
*/
struct TracepointSpec
{
std::string_view Trimmed = {}; // Input with leading/trailing whitespace removed = Trim(specString).
std::string_view SystemName = {}; // e.g. "user_events".
std::string_view EventName = {}; // e.g. "MyEvent" or "MyProvider_L2K1Gmygroup".
std::string_view Flags = {}; // e.g. "" or "flag1,flag2".
Expand Down Expand Up @@ -90,7 +91,14 @@ namespace tracepoint_control
Examples: "ProviderName_L1K1", or "SystemName:ProviderName_L1KffGgroup:Flags".
*/
explicit
TracepointSpec(std::string_view const specString);
TracepointSpec(std::string_view const specString) noexcept;

/*
Returns specString with all leading and trailing whitespace removed.
Uses the same definition of whitespace as the TracepointSpec constructor.
*/
static std::string_view
Trim(std::string_view const specString) noexcept;
};
}
// namespace tracepoint_control
Expand Down
1 change: 0 additions & 1 deletion libtracepoint-control-cpp/src/TracepointCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,6 @@ TracepointCache::PreregisterTracepointDefinition(TracepointSpec const& spec) noe
command = commandHeap.data();
}


snprintf(command, commandSize, "%.*s%s%.*s %s",
eventNameSize, spec.EventName.data(),
spec.Flags.empty() ? "" : ":",
Expand Down
41 changes: 29 additions & 12 deletions libtracepoint-control-cpp/src/TracepointSpec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ CountLeadingWhitespace(std::string_view str)
return pos;
}

TracepointSpec::TracepointSpec(std::string_view const specString)
TracepointSpec::TracepointSpec(std::string_view const specString) noexcept
{
bool identifier;
bool hasFields = false;
Expand All @@ -42,18 +42,10 @@ TracepointSpec::TracepointSpec(std::string_view const specString)
6. SystemName ':' EventName (':' Flags)? (WS Fields*)?
*/

// Trim trailing whitespace and semicolons.
size_t trimmedSize;
for (trimmedSize = specString.size(); trimmedSize != 0; trimmedSize -= 1)
{
if (!AsciiIsSpace(specString[trimmedSize - 1]))
{
break;
}
}
auto const trimmed = Trim(specString);
Trimmed = trimmed;

auto const trimmed = specString.substr(0, trimmedSize);
size_t pos = CountLeadingWhitespace(trimmed);
size_t pos = 0;
if (pos == trimmed.size())
{
Kind = TracepointSpecKind::Empty;
Expand Down Expand Up @@ -287,3 +279,28 @@ TracepointSpec::TracepointSpec(std::string_view const specString)
Kind = TracepointSpecKind::EventHeaderDefinition;
}
}

std::string_view
TracepointSpec::Trim(std::string_view const str) noexcept
{
size_t startPos;
size_t endPos;

for (startPos = 0; startPos != str.size(); startPos += 1)
{
if (!AsciiIsSpace(str[startPos]))
{
break;
}
}

for (endPos = str.size(); endPos != startPos; endPos -= 1)
{
if (!AsciiIsSpace(str[endPos - 1]))
{
break;
}
}

return str.substr(startPos, endPos - startPos);
}
87 changes: 38 additions & 49 deletions libtracepoint-control-cpp/tools/tracepoint-collect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,40 +121,29 @@ using namespace std::string_view_literals;
using namespace tracepoint_control;
using namespace tracepoint_decode;

struct Tracepoint
class Tracepoint
{
std::vector<char> storage;

public:

TracepointSpec spec;

Tracepoint(Tracepoint&&) = default;
Tracepoint& operator=(Tracepoint&&) = default;

explicit
Tracepoint(std::string_view line)
Tracepoint(std::string_view line)
{
size_t start = 0;
while (start != line.size() && AsciiIsSpace(line[start]))
{
start += 1;
}
auto const trimmedLine = TracepointSpec::Trim(line);

size_t end = line.size();
while (end != start && AsciiIsSpace(line[end - 1]))
if (!trimmedLine.empty())
{
end -= 1;
storage.assign(trimmedLine.begin(), trimmedLine.end());
}

storage.assign(line.begin() + start, line.begin() + end);
spec = TracepointSpec({ storage.data(), storage.size() });
}

private:

static bool
AsciiIsSpace(char ch)
{
return ch == ' ' || ('\t' <= ch && ch <= '\r');
}
};

struct Options
Expand Down Expand Up @@ -189,10 +178,10 @@ PrintStderrIf(bool condition, const char* format, ...)
}

static void
PushFrontDef(Options const& o, std::vector<Tracepoint>& tracepoints, Tracepoint tp)
PushFrontDef(Options const& o, std::vector<Tracepoint>& tracepoints, Tracepoint&& tp)
{
auto const& spec = tp.spec;
auto const& storage = tp.storage;
auto const trimmed = spec.Trimmed;
switch (tp.spec.Kind)
{
case TracepointSpecKind::Empty:
Expand All @@ -206,9 +195,9 @@ PushFrontDef(Options const& o, std::vector<Tracepoint>& tracepoints, Tracepoint
case TracepointSpecKind::Definition:
if (spec.SystemName != UserEventsSystemName)
{
PrintStderr("error: definition system name \"%.*s\" must be 'user_events': \"%.*s\".\n",
PrintStderr("error: definition system name \"%.*s\" must be 'user_events' (from \"%.*s\").\n",
(unsigned)spec.SystemName.size(), spec.SystemName.data(),
(unsigned)storage.size(), storage.data());
(unsigned)trimmed.size(), trimmed.data());
}
else
{
Expand All @@ -225,9 +214,9 @@ PushFrontDef(Options const& o, std::vector<Tracepoint>& tracepoints, Tracepoint
case TracepointSpecKind::EventHeaderDefinition:
if (spec.SystemName != UserEventsSystemName)
{
PrintStderr("error: eventheader system name \"%.*s\" must be 'user_events': \"%.*s\".\n",
PrintStderr("error: eventheader system name \"%.*s\" must be 'user_events' (from \"%.*s\").\n",
(unsigned)spec.SystemName.size(), spec.SystemName.data(),
(unsigned)storage.size(), storage.data());
(unsigned)trimmed.size(), trimmed.data());
}
else
{
Expand All @@ -240,60 +229,60 @@ PushFrontDef(Options const& o, std::vector<Tracepoint>& tracepoints, Tracepoint
}
break;
case TracepointSpecKind::ErrorIdentifierCannotHaveFields:
PrintStderr("error: identifier cannot have fields: \"%.*s\".\n",
(unsigned)storage.size(), storage.data());
PrintStderr("error: identifier cannot have fields (from \"%.*s\").\n",
(unsigned)trimmed.size(), trimmed.data());
break;
case TracepointSpecKind::ErrorIdentifierCannotHaveFlags:
PrintStderr("error: identifier cannot have flags: \"%.*s\".\n",
(unsigned)storage.size(), storage.data());
PrintStderr("error: identifier cannot have flags (from \"%.*s\").\n",
(unsigned)trimmed.size(), trimmed.data());
break;
case TracepointSpecKind::ErrorDefinitionCannotHaveColonAfterFlags:
PrintStderr("error: definition cannot have colon after flags: \"%.*s\".\n",
(unsigned)storage.size(), storage.data());
PrintStderr("error: definition cannot have colon after flags (from \"%.*s\").\n",
(unsigned)trimmed.size(), trimmed.data());
break;
case TracepointSpecKind::ErrorIdentifierEventNameEmpty:
PrintStderr("error: identifier event name is empty: \"%.*s\".\n",
(unsigned)storage.size(), storage.data());
PrintStderr("error: identifier event name is empty (from \"%.*s\").\n",
(unsigned)trimmed.size(), trimmed.data());
break;
case TracepointSpecKind::ErrorDefinitionEventNameEmpty:
PrintStderr("error: definition event name is empty: \"%.*s\".\n",
(unsigned)storage.size(), storage.data());
PrintStderr("error: definition event name is empty (from \"%.*s\").\n",
(unsigned)trimmed.size(), trimmed.data());
break;
case TracepointSpecKind::ErrorIdentifierEventNameInvalid:
PrintStderr("error: identifier event name \"%.*s\" is invalid: \"%.*s\".\n",
PrintStderr("error: identifier event name \"%.*s\" is invalid (from \"%.*s\").\n",
(unsigned)spec.EventName.size(), spec.EventName.data(),
(unsigned)storage.size(), storage.data());
(unsigned)trimmed.size(), trimmed.data());
break;
case TracepointSpecKind::ErrorDefinitionEventNameInvalid:
PrintStderr("error: definition event name \"%.*s\" is invalid: \"%.*s\".\n",
PrintStderr("error: definition event name \"%.*s\" is invalid (from \"%.*s\").\n",
(unsigned)spec.EventName.size(), spec.EventName.data(),
(unsigned)storage.size(), storage.data());
(unsigned)trimmed.size(), trimmed.data());
break;
case TracepointSpecKind::ErrorEventHeaderDefinitionEventNameInvalid:
PrintStderr("error: eventheader event name \"%.*s\" is invalid: \"%.*s\".\n",
PrintStderr("error: eventheader event name \"%.*s\" is invalid (from \"%.*s\").\n",
(unsigned)spec.EventName.size(), spec.EventName.data(),
(unsigned)storage.size(), storage.data());
(unsigned)trimmed.size(), trimmed.data());
PrintStderr("(error) If this was meant to be the name of an existing non-eventheader event, add a leading ':'.\n");
PrintStderr("(error) If this was meant to be the definition of a non-eventheader event, a Fields... section must be provided.\n");
PrintStderr("(error) If a non-eventheader event has no fields, use \" ;\" for Fields..., e.g. \"MyEvent ;\".\n");
break;
case TracepointSpecKind::ErrorIdentifierSystemNameEmpty:
PrintStderr("error: identifier system name is empty: \"%.*s\".\n",
(unsigned)storage.size(), storage.data());
PrintStderr("error: identifier system name is empty (from \"%.*s\").\n",
(unsigned)trimmed.size(), trimmed.data());
break;
case TracepointSpecKind::ErrorDefinitionSystemNameEmpty:
PrintStderr("error: definition system name is empty: \"%.*s\".\n",
(unsigned)storage.size(), storage.data());
PrintStderr("error: definition system name is empty (from \"%.*s\").\n",
(unsigned)trimmed.size(), trimmed.data());
break;
case TracepointSpecKind::ErrorIdentifierSystemNameInvalid:
PrintStderr("error: identifier system name \"%.*s\" is invalid: \"%.*s\".\n",
PrintStderr("error: identifier system name \"%.*s\" is invalid (from \"%.*s\").\n",
(unsigned)spec.SystemName.size(), spec.SystemName.data(),
(unsigned)storage.size(), storage.data());
(unsigned)trimmed.size(), trimmed.data());
break;
case TracepointSpecKind::ErrorDefinitionSystemNameInvalid:
PrintStderr("error: definition system name \"%.*s\" is invalid: \"%.*s\".\n",
PrintStderr("error: definition system name \"%.*s\" is invalid (from \"%.*s\").\n",
(unsigned)spec.SystemName.size(), spec.SystemName.data(),
(unsigned)storage.size(), storage.data());
(unsigned)trimmed.size(), trimmed.data());
break;
}
}
Expand Down
1 change: 0 additions & 1 deletion libtracepoint/include/tracepoint/tracepoint-provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,6 @@ A symbol declared by TPP_DECLARE_PROVIDER must later be defined in a
_tpp_EXTERN_C struct _tpp_provider_symbol ProviderSymbol __attribute__((visibility("hidden"))); /* Empty provider variable to help with code navigation. */ \
_tpp_EXTERN_C tracepoint_provider_state _tpp_PASTE2(_tppProvState_, ProviderSymbol) __attribute__((visibility("hidden"))) /* Actual provider variable is hidden behind prefix. */


/*
Macro TPP_DEFINE_PROVIDER(ProviderSymbol):
Invoke this macro to define the symbol for a provider. A provider is a
Expand Down

0 comments on commit e12bc6a

Please sign in to comment.