Commit 1b688cc0 authored by lawrencewu's avatar lawrencewu Committed by Commit bot

Add field trial dump and retrieval methods from shared memory

The Breadcrumbs project dumps system state to a persistent file so that it can
be analyzed after a crash.  We want to include in that the complete set of field
trials.  That means saving them to the file when the browser starts and being
able to retrieve them from the file on a future run; they will be loaded into a
protobuf and uploaded.

BUG=663414

Review-Url: https://codereview.chromium.org/2560723004
Cr-Commit-Position: refs/heads/master@{#437732}
parent 3459989e
This diff is collapsed.
...@@ -73,6 +73,7 @@ ...@@ -73,6 +73,7 @@
#include "base/memory/shared_memory.h" #include "base/memory/shared_memory.h"
#include "base/metrics/persistent_memory_allocator.h" #include "base/metrics/persistent_memory_allocator.h"
#include "base/observer_list_threadsafe.h" #include "base/observer_list_threadsafe.h"
#include "base/pickle.h"
#include "base/process/launch.h" #include "base/process/launch.h"
#include "base/strings/string_piece.h" #include "base/strings/string_piece.h"
#include "base/synchronization/lock.h" #include "base/synchronization/lock.h"
...@@ -133,6 +134,43 @@ class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> { ...@@ -133,6 +134,43 @@ class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> {
~State(); ~State();
}; };
// We create one FieldTrialEntry per field trial in shared memory, via
// AddToAllocatorWhileLocked. The FieldTrialEntry is followed by a
// base::Pickle object that we unpickle and read from. Any changes to this
// structure requires a bump in kFieldTrialType id defined in the .cc file.
struct BASE_EXPORT FieldTrialEntry {
// Expected size for 32/64-bit check.
static constexpr size_t kExpectedInstanceSize = 8;
// Whether or not this field trial is activated. This is really just a
// boolean but marked as a uint32_t for portability reasons.
uint32_t activated;
// Size of the pickled structure, NOT the total size of this entry.
uint32_t pickle_size;
// Calling this is only valid when the entry is initialized. That is, it
// resides in shared memory and has a pickle containing the trial name and
// group name following it.
bool GetTrialAndGroupName(StringPiece* trial_name,
StringPiece* group_name) const;
// Calling this is only valid when the entry is initialized as well. Reads
// the parameters following the trial and group name and stores them as
// key-value mappings in |params|.
bool GetParams(std::map<std::string, std::string>* params) const;
private:
// Returns an iterator over the data containing names and params.
PickleIterator GetPickleIterator() const;
// Takes the iterator and writes out the first two items into |trial_name|
// and |group_name|.
bool ReadStringPair(PickleIterator* iter,
StringPiece* trial_name,
StringPiece* group_name) const;
};
typedef std::vector<ActiveGroup> ActiveGroups; typedef std::vector<ActiveGroup> ActiveGroups;
// A return value to indicate that a given instance has not yet had a group // A return value to indicate that a given instance has not yet had a group
...@@ -577,6 +615,18 @@ class BASE_EXPORT FieldTrialList { ...@@ -577,6 +615,18 @@ class BASE_EXPORT FieldTrialList {
// Clears all the params in the allocator. // Clears all the params in the allocator.
static void ClearParamsFromSharedMemoryForTesting(); static void ClearParamsFromSharedMemoryForTesting();
// Dumps field trial state to an allocator so that it can be analyzed after a
// crash.
static void DumpAllFieldTrialsToPersistentAllocator(
PersistentMemoryAllocator* allocator);
// Retrieves field trial state from an allocator so that it can be analyzed
// after a crash. The pointers in the returned vector are into the persistent
// memory segment and so are only valid as long as the allocator is valid.
static std::vector<const FieldTrial::FieldTrialEntry*>
GetAllFieldTrialsFromPersistentAllocator(
PersistentMemoryAllocator const& allocator);
private: private:
// Allow tests to access our innards for testing purposes. // Allow tests to access our innards for testing purposes.
FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, InstantiateAllocator); FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, InstantiateAllocator);
...@@ -620,7 +670,8 @@ class BASE_EXPORT FieldTrialList { ...@@ -620,7 +670,8 @@ class BASE_EXPORT FieldTrialList {
// Adds the field trial to the allocator. Caller must hold a lock before // Adds the field trial to the allocator. Caller must hold a lock before
// calling this. // calling this.
static void AddToAllocatorWhileLocked(FieldTrial* field_trial); static void AddToAllocatorWhileLocked(PersistentMemoryAllocator* allocator,
FieldTrial* field_trial);
// Activate the corresponding field trial entry struct in shared memory. // Activate the corresponding field trial entry struct in shared memory.
static void ActivateFieldTrialEntryWhileLocked(FieldTrial* field_trial); static void ActivateFieldTrialEntryWhileLocked(FieldTrial* field_trial);
......
...@@ -1326,4 +1326,47 @@ TEST(FieldTrialListTest, ClearParamsFromSharedMemory) { ...@@ -1326,4 +1326,47 @@ TEST(FieldTrialListTest, ClearParamsFromSharedMemory) {
EXPECT_EQ("*Trial1/Group1/", check_string); EXPECT_EQ("*Trial1/Group1/", check_string);
} }
TEST(FieldTrialListTest, DumpAndFetchFromSharedMemory) {
std::string trial_name("Trial1");
std::string group_name("Group1");
// Create a field trial with some params.
FieldTrialList field_trial_list(nullptr);
FieldTrialList::CreateFieldTrial(trial_name, group_name);
std::map<std::string, std::string> params;
params["key1"] = "value1";
params["key2"] = "value2";
FieldTrialParamAssociator::GetInstance()->AssociateFieldTrialParams(
trial_name, group_name, params);
std::unique_ptr<base::SharedMemory> shm(new SharedMemory());
// 4 KiB is enough to hold the trials only created for this test.
shm.get()->CreateAndMapAnonymous(4 << 10);
// We _could_ use PersistentMemoryAllocator, this just has less params.
SharedPersistentMemoryAllocator allocator(std::move(shm), 1, "", false);
// Dump and subsequently retrieve the field trial to |allocator|.
FieldTrialList::DumpAllFieldTrialsToPersistentAllocator(&allocator);
std::vector<const FieldTrial::FieldTrialEntry*> entries =
FieldTrialList::GetAllFieldTrialsFromPersistentAllocator(allocator);
// Check that we have the entry we put in.
EXPECT_EQ(1u, entries.size());
const FieldTrial::FieldTrialEntry* entry = entries[0];
// Check that the trial and group names match.
StringPiece shm_trial_name;
StringPiece shm_group_name;
entry->GetTrialAndGroupName(&shm_trial_name, &shm_group_name);
EXPECT_EQ(trial_name, shm_trial_name);
EXPECT_EQ(group_name, shm_group_name);
// Check that the params match.
std::map<std::string, std::string> shm_params;
entry->GetParams(&shm_params);
EXPECT_EQ(2u, shm_params.size());
EXPECT_EQ("value1", shm_params["key1"]);
EXPECT_EQ("value2", shm_params["key2"]);
}
} // namespace base } // namespace base
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment