Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1118)

Unified Diff: base/metrics/field_trial.cc

Issue 2412113002: Use SharedPersistentMemoryAllocator to share field trial state (Closed)
Patch Set: add some documentation Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: base/metrics/field_trial.cc
diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc
index 4f3e0c76fcd58c606f3b98f90718f8dc63538d5f..96395f4fe26dbad71d4b2be87db31a3d5b96b0db 100644
--- a/base/metrics/field_trial.cc
+++ b/base/metrics/field_trial.cc
@@ -39,6 +39,20 @@ const char kActivationMarker = '*';
const bool kUseSharedMemoryForFieldTrials = false;
#endif
+const base::StringPiece kAllocatorName = "field_trial_allocator";
Alexei Svitkine (slow) 2016/10/12 20:41:00 const char kAllocatorName[] =
lawrencewu 2016/10/14 04:54:09 Done.
+const uint64_t kAllocatorId = 1337;
bcwhite 2016/10/13 14:28:02 If you don't care about this, you can always pass
lawrencewu 2016/10/14 04:54:09 Done.
+const uint64_t kFieldTrialType = 7331;
bcwhite 2016/10/13 14:28:02 Allocator ID is 64 bits but type IDs are only 32 b
lawrencewu 2016/10/14 04:54:09 Changed to the sha1sum + 1.
+
+// We create one FieldTrialEntry struct per field trial in shared memory (via
+// field_trial_allocator). It contains whether or not it's activated, and the
+// offset from the start of the allocated memory to the group name. We don't
+// need the offset into the trial name because that's always a fixed position
+// from the start of the struct.
bcwhite 2016/10/13 14:28:02 Document that two strings are appended to the end
lawrencewu 2016/10/14 04:54:09 I added a comment. While adding the name[1] member
bcwhite 2016/10/14 13:56:56 You could simply call it "strings" instead of "nam
lawrencewu 2016/10/19 16:21:28 Ack, will drop.
+struct FieldTrialEntry {
+ bool activated;
+ uint32_t group_name_offset;
+};
+
// Created a time value based on |year|, |month| and |day_of_month| parameters.
Time CreateTimeFromParams(int year, int month, int day_of_month) {
DCHECK_GT(year, 1970);
@@ -132,6 +146,8 @@ const int FieldTrial::kDefaultGroupNumber = 0;
bool FieldTrial::enable_benchmarking_ = false;
int FieldTrialList::kNoExpirationYear = 0;
+base::SharedPersistentMemoryAllocator* FieldTrialList::field_trial_allocator =
+ nullptr;
//------------------------------------------------------------------------------
// FieldTrial methods and members.
@@ -577,9 +593,7 @@ void FieldTrialList::CreateTrialsFromCommandLine(
#if defined(OS_WIN)
if (cmd_line.HasSwitch(field_trial_handle_switch)) {
std::string arg = cmd_line.GetSwitchValueASCII(field_trial_handle_switch);
- size_t token = arg.find(",");
- int field_trial_handle = std::stoi(arg.substr(0, token));
- int field_trial_length = std::stoi(arg.substr(token + 1, arg.length()));
+ int field_trial_handle = std::stoi(arg);
HANDLE handle = reinterpret_cast<HANDLE>(field_trial_handle);
base::SharedMemoryHandle shm_handle =
@@ -587,13 +601,11 @@ void FieldTrialList::CreateTrialsFromCommandLine(
// Gets deleted when it gets out of scope, but that's OK because we need it
// only for the duration of this call currently anyway.
bcwhite 2016/10/13 14:28:02 ... only for the duration of this method.
lawrencewu 2016/10/14 04:54:09 Fixed.
- base::SharedMemory shared_memory(shm_handle, false);
- shared_memory.Map(field_trial_length);
+ std::unique_ptr<base::SharedMemory> shm(
+ new base::SharedMemory(shm_handle, false));
+ shm.get()->Map(2 << 12); // 4 KiB = one page
bcwhite 2016/10/13 14:28:02 It looks like the comment is describing the consta
lawrencewu 2016/10/14 04:54:09 Whoops, that should really just be 4KiB, so the nu
- char* field_trial_state = static_cast<char*>(shared_memory.memory());
- bool result = FieldTrialList::CreateTrialsFromString(
- std::string(field_trial_state), std::set<std::string>());
- DCHECK(result);
+ FieldTrialList::CreateTrialsFromSharedMemory(std::move(shm));
return;
}
#endif
@@ -607,36 +619,61 @@ void FieldTrialList::CreateTrialsFromCommandLine(
}
// static
-std::unique_ptr<base::SharedMemory> FieldTrialList::CopyFieldTrialStateToFlags(
+void FieldTrialList::CreateTrialsFromSharedMemory(
+ std::unique_ptr<base::SharedMemory> shm) {
+ base::SharedPersistentMemoryAllocator shalloc(std::move(shm), kAllocatorId,
+ kAllocatorName, true);
bcwhite 2016/10/13 14:28:02 Since you're declaring the allocator as read-only,
bcwhite 2016/10/13 18:41:15 Actually, if you make this a "const" object then y
lawrencewu 2016/10/14 04:54:09 Ack, will ignore this.
bcwhite 2016/10/14 13:56:56 No, seriously. Make it "const". This case is exa
lawrencewu 2016/10/19 16:21:28 Oh, my bad, I misunderstood you (thought your seco
+ base::PersistentMemoryAllocator::Iterator iter(&shalloc);
+
+ uint32_t type;
+ base::SharedPersistentMemoryAllocator::Reference ref = iter.GetNext(&type);
bcwhite 2016/10/13 14:28:02 This will return a reference of any type but below
lawrencewu 2016/10/14 04:54:09 Done.
+ while (ref != base::SharedPersistentMemoryAllocator::kReferenceNull) {
bcwhite 2016/10/13 14:28:02 How about: while ((ref = iter.GetNextOfType(kFiel
lawrencewu 2016/10/14 04:54:09 Very nice, done.
+ char* src = shalloc.GetAsObject<char>(ref, kFieldTrialType);
+
+ FieldTrialEntry fte;
+ memcpy(&fte, src, sizeof(FieldTrialEntry));
Alexei Svitkine (slow) 2016/10/12 20:41:00 This memcpy is unnecessary. You can just cast src
lawrencewu 2016/10/14 04:54:09 Done.
+
+ char* trial_name = src + sizeof(FieldTrialEntry);
+ char* group_name = src + fte.group_name_offset;
Alexei Svitkine (slow) 2016/10/12 20:41:00 Can you make GetTrialName() and GetGroupName() acc
lawrencewu 2016/10/14 04:54:09 Done.
+
+ FieldTrial* trial = CreateFieldTrial(trial_name, group_name);
+ if (fte.activated) {
+ // Call |group()| to mark the trial as "used" and notify observers, if
+ // any. This is useful to ensure that field trials created in child
+ // processes are properly reported in crash reports.
+ trial->group();
+ }
+
+ ref = iter.GetNext(&type);
+ }
+}
+
+// static
+void FieldTrialList::CopyFieldTrialStateToFlags(
const char* field_trial_handle_switch,
base::CommandLine* cmd_line) {
- std::string field_trial_states;
- base::FieldTrialList::AllStatesToString(&field_trial_states);
- if (!field_trial_states.empty()) {
// Use shared memory to pass the state if the feature is enabled, otherwise
bcwhite 2016/10/13 14:28:02 Move comment inside #ifdef and fix indent.
lawrencewu 2016/10/14 04:54:09 Done.
// fallback to passing it via the command line as a string.
#if defined(OS_WIN)
- if (kUseSharedMemoryForFieldTrials) {
- std::unique_ptr<base::SharedMemory> shm(new base::SharedMemory());
- size_t length = field_trial_states.size() + 1;
- shm->CreateAndMapAnonymous(length);
- memcpy(shm->memory(), field_trial_states.c_str(), length);
-
- // HANDLE is just typedef'd to void *
- auto uintptr_handle =
- reinterpret_cast<std::uintptr_t>(shm->handle().GetHandle());
- std::string field_trial_handle =
- std::to_string(uintptr_handle) + "," + std::to_string(length);
-
- cmd_line->AppendSwitchASCII(field_trial_handle_switch,
- field_trial_handle);
- return shm;
- }
+ if (kUseSharedMemoryForFieldTrials) {
+ base::SharedMemory* shm = field_trial_allocator->shared_memory();
+
+ // HANDLE is just typedef'd to void *
+ auto uintptr_handle =
+ reinterpret_cast<std::uintptr_t>(shm->handle().GetHandle());
bcwhite 2016/10/13 14:28:02 Is std:: required for uintptr_t?
lawrencewu 2016/10/14 04:54:09 Looks like no. Omitted.
+ std::string field_trial_handle = std::to_string(uintptr_handle);
+
+ cmd_line->AppendSwitchASCII(field_trial_handle_switch, field_trial_handle);
bcwhite 2016/10/13 14:28:02 So... We can take a local void*, convert it to an
lawrencewu 2016/10/14 04:54:09 Yup, pretty hacky...added a comment and link to th
+ return;
+ }
#endif
+
+ std::string field_trial_states;
+ base::FieldTrialList::AllStatesToString(&field_trial_states);
+ if (!field_trial_states.empty()) {
cmd_line->AppendSwitchASCII(switches::kForceFieldTrials,
field_trial_states);
}
- return std::unique_ptr<base::SharedMemory>(nullptr);
}
// static
@@ -694,12 +731,64 @@ void FieldTrialList::NotifyFieldTrialGroupSelection(FieldTrial* field_trial) {
if (!field_trial->enable_field_trial_)
return;
+#if defined(OS_WIN)
bcwhite 2016/10/13 14:28:02 This isn't windows-specific code... Can we omit t
lawrencewu 2016/10/14 04:54:09 Yeah, we can do that. Omitted.
+ if (kUseSharedMemoryForFieldTrials) {
+ FieldTrialList::UpdateFieldTrialAllocator(field_trial);
+ }
+#endif
+
global_->observer_list_->Notify(
FROM_HERE, &FieldTrialList::Observer::OnFieldTrialGroupFinalized,
field_trial->trial_name(), field_trial->group_name_internal());
}
// static
+void FieldTrialList::UpdateFieldTrialAllocator(FieldTrial* field_trial) {
+ // Create the allocator if not already created.
+ if (field_trial_allocator == nullptr) {
bcwhite 2016/10/13 14:28:02 Are all calls to this method guaranteed to be on t
lawrencewu 2016/10/14 04:54:09 I'm not too sure about this. I'll do some investig
lawrencewu 2016/10/19 16:21:28 Turns out that it is not guaranteed to be thread-s
+ std::unique_ptr<base::SharedMemory> shm(new base::SharedMemory());
+ shm->CreateAndMapAnonymous(2 << 12); // 4 KiB = one page
+
+ field_trial_allocator = new base::SharedPersistentMemoryAllocator(
+ std::move(shm), kAllocatorId, kAllocatorName, false);
+ }
+
+ // We allocate just enough memory to fit the FTE struct, which contains
+ // whether or not the field trial is activated and the offset from the start
+ // of the struct to the group name.
+ // By the end of this function, the piece of allocated memory will look like
+ // this:
+ // ---------------------------------
+ // | fte | trial_name | group_name |
+ // ---------------------------------
+ // with fte pointing ^
+ FieldTrial::State trial_state;
+ field_trial->GetState(&trial_state);
+
+ std::string trial_name;
+ trial_state.trial_name.CopyToString(&trial_name);
+ size_t trial_name_size = trial_name.size() + 1;
+
+ std::string group_name;
+ trial_state.group_name.CopyToString(&group_name);
+ size_t group_name_size = group_name.size() + 1;
+
+ uint32_t trial_name_offset = sizeof(FieldTrialEntry);
+ uint32_t group_name_offset = trial_name_offset + trial_name_size;
+ size_t size = sizeof(FieldTrialEntry) + trial_name_size + group_name_size;
+
+ base::SharedPersistentMemoryAllocator::Reference ref =
+ field_trial_allocator->Allocate(size, kFieldTrialType);
+ field_trial_allocator->MakeIterable(ref);
bcwhite 2016/10/13 14:28:02 Move this to the end. Don't make it iterable unti
lawrencewu 2016/10/14 04:54:09 Done.
+
+ char* dst = field_trial_allocator->GetAsObject<char>(ref, kFieldTrialType);
+ FieldTrialEntry fte = {trial_state.activated, group_name_offset};
+ memcpy(dst, &fte, sizeof(FieldTrialEntry));
+ memcpy(dst + trial_name_offset, trial_name.c_str(), trial_name_size);
+ memcpy(dst + group_name_offset, group_name.c_str(), group_name_size);
+}
+
+// static
size_t FieldTrialList::GetFieldTrialCount() {
if (!global_)
return 0;

Powered by Google App Engine
This is Rietveld 408576698