// Copyright (c) 2015 Baidu, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Author: Ge,Jun (gejun@baidu.com) // Date: Mon Dec 14 19:12:30 CST 2015 #ifndef BVAR_COLLECTOR_H #define BVAR_COLLECTOR_H #include "butil/containers/linked_list.h" #include "butil/fast_rand.h" #include "butil/time.h" #include "butil/atomicops.h" #include "bvar/passive_status.h" namespace bvar { // Containing the context for limiting sampling speed. struct CollectorSpeedLimit { // [Managed by Collector, don't change!] size_t sampling_range; bool ever_grabbed; butil::static_atomic<int> count_before_grabbed; int64_t first_sample_real_us; }; static const size_t COLLECTOR_SAMPLING_BASE = 16384; #define BVAR_COLLECTOR_SPEED_LIMIT_INITIALIZER \ { ::bvar::COLLECTOR_SAMPLING_BASE, false, BUTIL_STATIC_ATOMIC_INIT(0), 0 } class Collected; // For processing samples in batch before dumping. class CollectorPreprocessor { public: virtual void process(std::vector<Collected*>& samples) = 0; }; // Steps for sampling and dumping sth: // 1. Implement Collected // 2. Create an instance and fill in data. // 3. submit() the instance. class Collected : public butil::LinkNode<Collected> { public: virtual ~Collected() {} // Sumbit the sample for later dumping, a sample can only be submitted once. // submit() is implemented as writing a value to bvar::Reducer which does // not compete globally. This function generally does not alter the // interleaving status of threads even in highly contended situations. // You should also create the sample using a malloc() impl. that are // unlikely to contend, keeping interruptions minimal. // `cpuwide_us' should be got from butil::cpuwide_time_us(). If it's far // from the timestamp updated by collecting thread(which basically means // the thread is not scheduled by OS in time), this sample is directly // destroy()-ed to avoid memory explosion. void submit(int64_t cpuwide_us); void submit() { submit(butil::cpuwide_time_us()); } // Implement this method to dump the sample into files and destroy it. // This method is called in a separate thread and can be blocked // indefinitely long(not recommended). If too many samples wait for // this funcion due to previous sample's blocking, they'll be destroy()-ed. // If you need to run destruction code upon thread's exit, use // butil::thread_atexit. Dumping thread run this function in batch, each // batch is counted as one "round", `round_index' is the round that // dumping thread is currently at, counting from 1. virtual void dump_and_destroy(size_t round_index) = 0; // Destroy the sample. Will be called for at most once. Since dumping // thread generally quits upon the termination of program, some samples // are directly recycled along with program w/o calling destroy(). virtual void destroy() = 0; // Returns an object to control #samples collected per second. // If NULL is returned, samples collected per second is limited by a // global speed limit shared with other samples also returning NULL. // All instances of a subclass of Collected should return a same instance // of CollectorSpeedLimit. The instance should remain valid during lifetime // of program. virtual CollectorSpeedLimit* speed_limit() = 0; // If this method returns a non-NULL instance, it will be applied to // samples in batch before dumping. You can sort or shuffle the samples // in the impl. // All instances of a subclass of Collected should return a same instance // of CollectorPreprocessor. The instance should remain valid during // lifetime of program. virtual CollectorPreprocessor* preprocessor() { return NULL; } }; // To know if an instance should be sampled. // Returns a positive number when the object should be sampled, 0 otherwise. // The number is approximately the current probability of sampling times // COLLECTOR_SAMPLING_BASE, it varies from seconds to seconds being adjusted // by collecting thread to control the samples collected per second. // This function should cost less than 10ns in most cases. inline size_t is_collectable(CollectorSpeedLimit* speed_limit) { if (speed_limit->ever_grabbed) { // most common case const size_t sampling_range = speed_limit->sampling_range; // fast_rand is faster than fast_rand_in if ((butil::fast_rand() & (COLLECTOR_SAMPLING_BASE - 1)) >= sampling_range) { return 0; } return sampling_range; } // Slower, only runs before -bvar_collector_expected_per_second samples are // collected to calculate a more reasonable sampling_range for the type. extern size_t is_collectable_before_first_time_grabbed(CollectorSpeedLimit*); return is_collectable_before_first_time_grabbed(speed_limit); } // An utility for displaying current sampling ratio according to speed limit. class DisplaySamplingRatio { public: DisplaySamplingRatio(const char* name, const CollectorSpeedLimit*); private: bvar::PassiveStatus<double> _var; }; } // namespace bvar #endif // BVAR_COLLECTOR_H