Bug 1735397 - mozilla::profiler::detail::FilterHasPid and FiltersExcludePid - r=florian

These will be used to check special thread filter rules around "pid:..." filters.

Differential Revision: https://phabricator.services.mozilla.com/D135853
This commit is contained in:
Gerald Squelart 2022-02-08 12:47:52 +00:00
parent 7e96f5a4d3
commit ca8a203ae8
3 changed files with 190 additions and 2 deletions

View File

@ -6,8 +6,87 @@
#include "mozilla/BaseAndGeckoProfilerDetail.h"
#include <limits>
#include <string_view>
namespace mozilla::profiler::detail {
; // TODO Add code here.
constexpr std::string_view scPidPrefix = "pid:";
// Convert a C string to a BaseProfilerProcessId. Return unspecified
// BaseProfilerProcessId if the string is not exactly a valid pid.
static baseprofiler::BaseProfilerProcessId StringToPid(const char* aString) {
if (!aString || aString[0] == '\0') {
// Null or empty.
return baseprofiler::BaseProfilerProcessId{};
}
if (aString[0] == '0') {
if (aString[1] != '\0') {
// Don't accept leading zeroes.
return baseprofiler::BaseProfilerProcessId{};
}
return baseprofiler::BaseProfilerProcessId::FromNumber(0);
}
using PidNumber = baseprofiler::BaseProfilerProcessId::NumberType;
PidNumber pid = 0;
for (;;) {
const char c = *aString;
if (c == '\0') {
break;
}
if (c < '0' || c > '9') {
// Only accept decimal digits.
return baseprofiler::BaseProfilerProcessId{};
}
static_assert(!std::numeric_limits<PidNumber>::is_signed,
"The following relies on unsigned arithmetic");
PidNumber newPid = pid * 10u + PidNumber(c - '0');
if (newPid < pid) {
// Unsigned overflow.
return baseprofiler::BaseProfilerProcessId{};
}
pid = newPid;
++aString;
}
return baseprofiler::BaseProfilerProcessId::FromNumber(pid);
}
[[nodiscard]] MFBT_API bool FilterHasPid(
const char* aFilter, baseprofiler::BaseProfilerProcessId aPid) {
if (strncmp(aFilter, scPidPrefix.data(), scPidPrefix.length()) != 0) {
// The filter is not starting with "pid:".
return false;
}
return StringToPid(aFilter + scPidPrefix.length()) == aPid;
}
[[nodiscard]] MFBT_API bool FiltersExcludePid(
Span<const char* const> aFilters,
baseprofiler::BaseProfilerProcessId aPid) {
if (aFilters.empty()) {
return false;
}
// First, check if the list only contains "pid:..." strings.
for (const char* const filter : aFilters) {
if (strncmp(filter, scPidPrefix.data(), scPidPrefix.length()) != 0) {
// At least one filter is *not* a "pid:...", our pid is not excluded.
return false;
}
}
// Here, all filters start with "pid:". Check if the given pid is included.
for (const char* const filter : aFilters) {
if (StringToPid(filter + scPidPrefix.length()) == aPid) {
// Our pid is present, so it's not excluded.
return false;
}
}
// Our pid was not in a list of only pids, so it's excluded.
return true;
}
} // namespace mozilla::profiler::detail

View File

@ -13,9 +13,30 @@
#ifndef BaseAndGeckoProfilerDetail_h
#define BaseAndGeckoProfilerDetail_h
#include "mozilla/BaseProfilerUtils.h"
#include "mozilla/Span.h"
#include "mozilla/Types.h"
namespace mozilla::profiler::detail {
; // TODO Add APIs here.
// True if the filter is exactly "pid:<aPid>".
[[nodiscard]] MFBT_API bool FilterHasPid(
const char* aFilter, baseprofiler::BaseProfilerProcessId aPid =
baseprofiler::profiler_current_process_id());
// Only true if the filters only contain "pid:..." strings, and *none* of them
// is exactly "pid:<aPid>". E.g.:
// - [], 123 -> false (no pids)
// - ["main"], 123 -> false (not all pids)
// - ["main", "pid:123"], 123 -> false (not all pids)
// - ["pid:123"], 123 -> false (all pids, including "pid:123")
// - ["pid:123", "pid:456"], 123 -> false (all pids, including "pid:123")
// - ["pid:456"], 123 -> true (all pids, but no "pid:123")
// - ["pid:456", "pid:789"], 123 -> true (all pids, but no "pid:123")
[[nodiscard]] MFBT_API bool FiltersExcludePid(
Span<const char* const> aFilters,
baseprofiler::BaseProfilerProcessId aPid =
baseprofiler::profiler_current_process_id());
} // namespace mozilla::profiler::detail

View File

@ -7,6 +7,7 @@
#include "BaseProfiler.h"
#include "mozilla/Attributes.h"
#include "mozilla/BaseAndGeckoProfilerDetail.h"
#include "mozilla/BaseProfileJSONWriter.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/ProgressLogger.h"
@ -229,6 +230,92 @@ void TestProfilerUtils() {
printf("TestProfilerUtils done\n");
}
void TestBaseAndProfilerDetail() {
printf("TestBaseAndProfilerDetail...\n");
{
using mozilla::profiler::detail::FilterHasPid;
const auto pid123 =
mozilla::baseprofiler::BaseProfilerProcessId::FromNumber(123);
MOZ_RELEASE_ASSERT(FilterHasPid("pid:123", pid123));
MOZ_RELEASE_ASSERT(!FilterHasPid("", pid123));
MOZ_RELEASE_ASSERT(!FilterHasPid(" ", pid123));
MOZ_RELEASE_ASSERT(!FilterHasPid("123", pid123));
MOZ_RELEASE_ASSERT(!FilterHasPid("pid", pid123));
MOZ_RELEASE_ASSERT(!FilterHasPid("pid:", pid123));
MOZ_RELEASE_ASSERT(!FilterHasPid("pid=123", pid123));
MOZ_RELEASE_ASSERT(!FilterHasPid("pid:123 ", pid123));
MOZ_RELEASE_ASSERT(!FilterHasPid("pid: 123", pid123));
MOZ_RELEASE_ASSERT(!FilterHasPid("pid:0123", pid123));
MOZ_RELEASE_ASSERT(!FilterHasPid("pid:0000000000000000000000123", pid123));
MOZ_RELEASE_ASSERT(!FilterHasPid("pid:12", pid123));
MOZ_RELEASE_ASSERT(!FilterHasPid("pid:1234", pid123));
MOZ_RELEASE_ASSERT(!FilterHasPid("pid:0", pid123));
using PidNumber = mozilla::baseprofiler::BaseProfilerProcessId::NumberType;
const PidNumber maxNumber = std::numeric_limits<PidNumber>::max();
const auto maxPid =
mozilla::baseprofiler::BaseProfilerProcessId::FromNumber(maxNumber);
const std::string maxPidString = "pid:" + std::to_string(maxNumber);
MOZ_RELEASE_ASSERT(FilterHasPid(maxPidString.c_str(), maxPid));
const std::string tooBigPidString = maxPidString + "0";
MOZ_RELEASE_ASSERT(!FilterHasPid(tooBigPidString.c_str(), maxPid));
}
{
using mozilla::profiler::detail::FiltersExcludePid;
const auto pid123 =
mozilla::baseprofiler::BaseProfilerProcessId::FromNumber(123);
MOZ_RELEASE_ASSERT(
!FiltersExcludePid(mozilla::Span<const char*>{}, pid123));
{
const char* const filters[] = {"main"};
MOZ_RELEASE_ASSERT(!FiltersExcludePid(filters, pid123));
}
{
const char* const filters[] = {"main", "pid:123"};
MOZ_RELEASE_ASSERT(!FiltersExcludePid(filters, pid123));
}
{
const char* const filters[] = {"main", "pid:456"};
MOZ_RELEASE_ASSERT(!FiltersExcludePid(filters, pid123));
}
{
const char* const filters[] = {"pid:123"};
MOZ_RELEASE_ASSERT(!FiltersExcludePid(filters, pid123));
}
{
const char* const filters[] = {"pid:123", "pid:456"};
MOZ_RELEASE_ASSERT(!FiltersExcludePid(filters, pid123));
}
{
const char* const filters[] = {"pid:456", "pid:123"};
MOZ_RELEASE_ASSERT(!FiltersExcludePid(filters, pid123));
}
{
const char* const filters[] = {"pid:456"};
MOZ_RELEASE_ASSERT(FiltersExcludePid(filters, pid123));
}
{
const char* const filters[] = {"pid:456", "pid:789"};
MOZ_RELEASE_ASSERT(FiltersExcludePid(filters, pid123));
}
}
printf("TestBaseAndProfilerDetail done\n");
}
void TestProportionValue() {
printf("TestProportionValue...\n");
@ -5129,6 +5216,7 @@ int main()
#endif // MOZ_GECKO_PROFILER
TestProfilerUtils();
TestBaseAndProfilerDetail();
TestProportionValue();
TestProgressLogger();
// Note that there are two `TestProfiler{,Markers}` functions above, depending