blob: a334f5da39e6c87b1d5b94454a8ad1e351dc6499 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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.
// 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