OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/metrics/field_trial.h" | 5 #include "base/metrics/field_trial.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <utility> | 8 #include <utility> |
9 | 9 |
10 #include "base/base_switches.h" | 10 #include "base/base_switches.h" |
(...skipping 18 matching lines...) Expand all Loading... | |
29 const char kPersistentStringSeparator = '/'; // Currently a slash. | 29 const char kPersistentStringSeparator = '/'; // Currently a slash. |
30 | 30 |
31 // Define a marker character to be used as a prefix to a trial name on the | 31 // Define a marker character to be used as a prefix to a trial name on the |
32 // command line which forces its activation. | 32 // command line which forces its activation. |
33 const char kActivationMarker = '*'; | 33 const char kActivationMarker = '*'; |
34 | 34 |
35 // Use shared memory to communicate field trial (experiment) state. Set to false | 35 // Use shared memory to communicate field trial (experiment) state. Set to false |
36 // for now while the implementation is fleshed out (e.g. data format, single | 36 // for now while the implementation is fleshed out (e.g. data format, single |
37 // shared memory segment). See https://codereview.chromium.org/2365273004/ and | 37 // shared memory segment). See https://codereview.chromium.org/2365273004/ and |
38 // crbug.com/653874 | 38 // crbug.com/653874 |
39 const bool kUseSharedMemoryForFieldTrials = false; | 39 const bool kUseSharedMemoryForFieldTrials = true; |
40 | 40 |
41 // Constants for the field trial allocator. | 41 // Constants for the field trial allocator. |
42 const char kAllocatorName[] = "FieldTrialAllocator"; | 42 const char kAllocatorName[] = "FieldTrialAllocator"; |
43 const uint32_t kFieldTrialType = 0xABA17E13 + 1; // SHA1(FieldTrialEntry) v1 | 43 const uint32_t kFieldTrialType = 0xABA17E13 + 1; // SHA1(FieldTrialEntry) v1 |
44 #if !defined(OS_NACL) | 44 #if !defined(OS_NACL) |
45 const size_t kFieldTrialAllocationSize = 4 << 10; // 4 KiB = one page | 45 const size_t kFieldTrialAllocationSize = 4 << 10; // 4 KiB = one page |
46 #endif | 46 #endif |
47 | 47 |
48 // We create one FieldTrialEntry struct per field trial in shared memory (via | 48 // We create one FieldTrialEntry struct per field trial in shared memory (via |
49 // field_trial_allocator_). It contains whether or not it's activated, and the | 49 // field_trial_allocator_). It contains whether or not it's activated, and the |
(...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
243 | 243 |
244 DCHECK_LE(accumulated_group_probability_, divisor_); | 244 DCHECK_LE(accumulated_group_probability_, divisor_); |
245 if (group_ == kNotFinalized && accumulated_group_probability_ > random_) { | 245 if (group_ == kNotFinalized && accumulated_group_probability_ > random_) { |
246 // This is the group that crossed the random line, so we do the assignment. | 246 // This is the group that crossed the random line, so we do the assignment. |
247 SetGroupChoice(name, next_group_number_); | 247 SetGroupChoice(name, next_group_number_); |
248 } | 248 } |
249 return next_group_number_++; | 249 return next_group_number_++; |
250 } | 250 } |
251 | 251 |
252 int FieldTrial::group() { | 252 int FieldTrial::group() { |
253 FinalizeGroupChoice(); | 253 FinalizeGroupChoice(false); |
254 if (trial_registered_) | 254 if (trial_registered_) |
255 FieldTrialList::NotifyFieldTrialGroupSelection(this); | 255 FieldTrialList::NotifyFieldTrialGroupSelection(this); |
256 return group_; | 256 return group_; |
257 } | 257 } |
258 | 258 |
259 const std::string& FieldTrial::group_name() { | 259 const std::string& FieldTrial::group_name() { |
260 // Call |group()| to ensure group gets assigned and observers are notified. | 260 // Call |group()| to ensure group gets assigned and observers are notified. |
261 group(); | 261 group(); |
262 DCHECK(!group_name_.empty()); | 262 DCHECK(!group_name_.empty()); |
263 return group_name_; | 263 return group_name_; |
264 } | 264 } |
265 | 265 |
266 const std::string& FieldTrial::GetGroupNameWithoutActivation() { | 266 const std::string& FieldTrial::GetGroupNameWithoutActivation() { |
267 FinalizeGroupChoice(); | 267 FinalizeGroupChoice(false); |
268 return group_name_; | 268 return group_name_; |
269 } | 269 } |
270 | 270 |
271 void FieldTrial::SetForced() { | 271 void FieldTrial::SetForced() { |
272 // We might have been forced before (e.g., by CreateFieldTrial) and it's | 272 // We might have been forced before (e.g., by CreateFieldTrial) and it's |
273 // first come first served, e.g., command line switch has precedence. | 273 // first come first served, e.g., command line switch has precedence. |
274 if (forced_) | 274 if (forced_) |
275 return; | 275 return; |
276 | 276 |
277 // And we must finalize the group choice before we mark ourselves as forced. | 277 // And we must finalize the group choice before we mark ourselves as forced. |
278 FinalizeGroupChoice(); | 278 FinalizeGroupChoice(false); |
279 forced_ = true; | 279 forced_ = true; |
280 } | 280 } |
281 | 281 |
282 // static | 282 // static |
283 void FieldTrial::EnableBenchmarking() { | 283 void FieldTrial::EnableBenchmarking() { |
284 DCHECK_EQ(0u, FieldTrialList::GetFieldTrialCount()); | 284 DCHECK_EQ(0u, FieldTrialList::GetFieldTrialCount()); |
285 enable_benchmarking_ = true; | 285 enable_benchmarking_ = true; |
286 } | 286 } |
287 | 287 |
288 // static | 288 // static |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
326 | 326 |
327 void FieldTrial::SetGroupChoice(const std::string& group_name, int number) { | 327 void FieldTrial::SetGroupChoice(const std::string& group_name, int number) { |
328 group_ = number; | 328 group_ = number; |
329 if (group_name.empty()) | 329 if (group_name.empty()) |
330 StringAppendF(&group_name_, "%d", group_); | 330 StringAppendF(&group_name_, "%d", group_); |
331 else | 331 else |
332 group_name_ = group_name; | 332 group_name_ = group_name; |
333 DVLOG(1) << "Field trial: " << trial_name_ << " Group choice:" << group_name_; | 333 DVLOG(1) << "Field trial: " << trial_name_ << " Group choice:" << group_name_; |
334 } | 334 } |
335 | 335 |
336 void FieldTrial::FinalizeGroupChoice() { | 336 void FieldTrial::FinalizeGroupChoice(bool is_locked) { |
337 if (group_ != kNotFinalized) | 337 if (group_ != kNotFinalized) |
338 return; | 338 return; |
339 accumulated_group_probability_ = divisor_; | 339 accumulated_group_probability_ = divisor_; |
340 // Here it's OK to use |kDefaultGroupNumber| since we can't be forced and not | 340 // Here it's OK to use |kDefaultGroupNumber| since we can't be forced and not |
341 // finalized. | 341 // finalized. |
342 DCHECK(!forced_); | 342 DCHECK(!forced_); |
343 SetGroupChoice(default_group_name_, kDefaultGroupNumber); | 343 SetGroupChoice(default_group_name_, kDefaultGroupNumber); |
344 | 344 |
345 // Add the field trial to shared memory. | 345 // Add the field trial to shared memory. |
346 if (kUseSharedMemoryForFieldTrials) | 346 if (kUseSharedMemoryForFieldTrials) |
347 FieldTrialList::OnGroupFinalized(this); | 347 FieldTrialList::OnGroupFinalized(is_locked, this); |
348 } | 348 } |
349 | 349 |
350 bool FieldTrial::GetActiveGroup(ActiveGroup* active_group) const { | 350 bool FieldTrial::GetActiveGroup(ActiveGroup* active_group) const { |
351 if (!group_reported_ || !enable_field_trial_) | 351 if (!group_reported_ || !enable_field_trial_) |
352 return false; | 352 return false; |
353 DCHECK_NE(group_, kNotFinalized); | 353 DCHECK_NE(group_, kNotFinalized); |
354 active_group->trial_name = trial_name_; | 354 active_group->trial_name = trial_name_; |
355 active_group->group_name = group_name_; | 355 active_group->group_name = group_name_; |
356 return true; | 356 return true; |
357 } | 357 } |
358 | 358 |
359 bool FieldTrial::GetState(State* field_trial_state) { | 359 bool FieldTrial::GetState(State* field_trial_state) { |
360 if (!enable_field_trial_) | 360 if (!enable_field_trial_) |
361 return false; | 361 return false; |
362 FinalizeGroupChoice(); | 362 FinalizeGroupChoice(false); |
363 field_trial_state->trial_name = trial_name_; | 363 field_trial_state->trial_name = trial_name_; |
364 field_trial_state->group_name = group_name_; | 364 field_trial_state->group_name = group_name_; |
365 field_trial_state->activated = group_reported_; | 365 field_trial_state->activated = group_reported_; |
366 return true; | |
367 } | |
368 | |
369 bool FieldTrial::GetStateWhileLocked(State* field_trial_state) { | |
370 if (!enable_field_trial_) | |
371 return false; | |
372 FinalizeGroupChoice(true); | |
Alexei Svitkine (slow)
2016/10/27 21:40:43
How about making the call that takes the param Fin
lawrencewu
2016/10/28 17:20:21
Done.
| |
373 field_trial_state->trial_name = trial_name_; | |
374 field_trial_state->group_name = group_name_; | |
375 field_trial_state->activated = group_reported_; | |
366 return true; | 376 return true; |
367 } | 377 } |
368 | 378 |
369 //------------------------------------------------------------------------------ | 379 //------------------------------------------------------------------------------ |
370 // FieldTrialList methods and members. | 380 // FieldTrialList methods and members. |
371 | 381 |
372 // static | 382 // static |
373 FieldTrialList* FieldTrialList::global_ = NULL; | 383 FieldTrialList* FieldTrialList::global_ = NULL; |
374 | 384 |
375 // static | 385 // static |
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
539 } | 549 } |
540 | 550 |
541 // static | 551 // static |
542 void FieldTrialList::AllStatesToString(std::string* output) { | 552 void FieldTrialList::AllStatesToString(std::string* output) { |
543 if (!global_) | 553 if (!global_) |
544 return; | 554 return; |
545 AutoLock auto_lock(global_->lock_); | 555 AutoLock auto_lock(global_->lock_); |
546 | 556 |
547 for (const auto& registered : global_->registered_) { | 557 for (const auto& registered : global_->registered_) { |
548 FieldTrial::State trial; | 558 FieldTrial::State trial; |
549 if (!registered.second->GetState(&trial)) | 559 if (!registered.second->GetStateWhileLocked(&trial)) |
550 continue; | 560 continue; |
551 DCHECK_EQ(std::string::npos, | 561 DCHECK_EQ(std::string::npos, |
552 trial.trial_name.find(kPersistentStringSeparator)); | 562 trial.trial_name.find(kPersistentStringSeparator)); |
553 DCHECK_EQ(std::string::npos, | 563 DCHECK_EQ(std::string::npos, |
554 trial.group_name.find(kPersistentStringSeparator)); | 564 trial.group_name.find(kPersistentStringSeparator)); |
555 if (trial.activated) | 565 if (trial.activated) |
556 output->append(1, kActivationMarker); | 566 output->append(1, kActivationMarker); |
557 trial.trial_name.AppendToString(output); | 567 trial.trial_name.AppendToString(output); |
558 output->append(1, kPersistentStringSeparator); | 568 output->append(1, kPersistentStringSeparator); |
559 trial.group_name.AppendToString(output); | 569 trial.group_name.AppendToString(output); |
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
746 } | 756 } |
747 | 757 |
748 // static | 758 // static |
749 void FieldTrialList::RemoveObserver(Observer* observer) { | 759 void FieldTrialList::RemoveObserver(Observer* observer) { |
750 if (!global_) | 760 if (!global_) |
751 return; | 761 return; |
752 global_->observer_list_->RemoveObserver(observer); | 762 global_->observer_list_->RemoveObserver(observer); |
753 } | 763 } |
754 | 764 |
755 // static | 765 // static |
756 void FieldTrialList::OnGroupFinalized(FieldTrial* field_trial) { | 766 void FieldTrialList::OnGroupFinalized(bool is_locked, FieldTrial* field_trial) { |
757 if (!global_) | 767 if (!global_) |
758 return; | 768 return; |
759 AutoLock auto_lock(global_->lock_); | 769 if (is_locked) { |
760 AddToAllocatorWhileLocked(field_trial); | 770 AddToAllocatorWhileLocked(field_trial); |
771 } else { | |
772 AutoLock auto_lock(global_->lock_); | |
773 AddToAllocatorWhileLocked(field_trial); | |
774 } | |
761 } | 775 } |
762 | 776 |
763 // static | 777 // static |
764 void FieldTrialList::NotifyFieldTrialGroupSelection(FieldTrial* field_trial) { | 778 void FieldTrialList::NotifyFieldTrialGroupSelection(FieldTrial* field_trial) { |
765 if (!global_) | 779 if (!global_) |
766 return; | 780 return; |
767 | 781 |
768 { | 782 { |
769 AutoLock auto_lock(global_->lock_); | 783 AutoLock auto_lock(global_->lock_); |
770 if (field_trial->group_reported_) | 784 if (field_trial->group_reported_) |
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
944 return; | 958 return; |
945 } | 959 } |
946 AutoLock auto_lock(global_->lock_); | 960 AutoLock auto_lock(global_->lock_); |
947 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); | 961 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); |
948 trial->AddRef(); | 962 trial->AddRef(); |
949 trial->SetTrialRegistered(); | 963 trial->SetTrialRegistered(); |
950 global_->registered_[trial->trial_name()] = trial; | 964 global_->registered_[trial->trial_name()] = trial; |
951 } | 965 } |
952 | 966 |
953 } // namespace base | 967 } // namespace base |
OLD | NEW |