blob: 7178f20a7be3330556a064e2c85883006f4de38f [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.
#ifndef BIGO_BRPC_IOBUF_PROFILER_H
#define BIGO_BRPC_IOBUF_PROFILER_H
#include <unordered_map>
#include <unordered_set>
#include "butil/iobuf.h"
#include "butil/object_pool.h"
#include "butil/threading/simple_thread.h"
#include "butil/threading/simple_thread.h"
#include "butil/memory/singleton.h"
#include "butil/containers/flat_map.h"
#include "butil/containers/mpsc_queue.h"
namespace butil {
struct IOBufSample;
typedef std::shared_ptr<IOBufSample> IOBufRefSampleSharedPtr;
struct IOBufSample {
IOBufSample* next;
IOBuf::Block* block;
int64_t count; // // reference count of the block.
void* stack[28]; // backtrace.
int nframes; // num of frames in stack.
static IOBufSample* New() {
return get_object<IOBufSample>();
}
static IOBufSample* Copy(IOBufSample* ref);
static IOBufRefSampleSharedPtr CopyAndSharedWithDestroyer(IOBufSample* ref);
static void Destroy(IOBufSample* ref) {
ref->_hash_code = 0;
return_object(ref);
}
size_t stack_hash_code() const;
private:
friend ObjectPool<IOBufSample>;
IOBufSample()
: next(NULL)
, block(NULL)
, count(0)
, stack{}
, nframes(0)
, _hash_code(0) {}
~IOBufSample() = default;
mutable uint32_t _hash_code; // For combining samples with hashmap.
};
BAIDU_CASSERT(sizeof(IOBufSample) == 256, be_friendly_to_allocator);
namespace detail {
// Functor to compare IOBufRefSample.
template <typename T>
struct IOBufSampleEqual {
bool operator()(const T& c1, const T& c2) const {
return c1->stack_hash_code() ==c2->stack_hash_code() &&
c1->nframes == c2->nframes &&
memcmp(c1->stack, c2->stack, sizeof(void*) * c1->nframes) == 0;
}
};
// Functor to hash IOBufRefSample.
template <typename T>
struct IOBufSampleHash {
size_t operator()(const T& c) const {
return c->stack_hash_code();
}
};
struct Destroyer {
void operator()(IOBufSample* ref) const {
if (ref) {
IOBufSample::Destroy(ref);
}
}
};
}
class IOBufProfiler : public butil::SimpleThread {
public:
static IOBufProfiler* GetInstance();
// Submit the IOBuf sample along with stacktrace.
void Submit(IOBufSample* s);
// Dump IOBuf sample to map.
void Dump(IOBufSample* s);
// Write buffered data into resulting file.
void Flush2Disk(const char* filename);
void StopAndJoin();
private:
friend struct DefaultSingletonTraits<IOBufProfiler>;
typedef butil::FlatMap<IOBufSample*,
IOBufRefSampleSharedPtr,
detail::IOBufSampleHash<IOBufSample*>,
detail::IOBufSampleEqual<IOBufSample*>> IOBufRefMap;
// <iobuf stack, ref count>
typedef butil::FlatMap<IOBufRefSampleSharedPtr,
int64_t,
detail::IOBufSampleHash<IOBufRefSampleSharedPtr>,
detail::IOBufSampleEqual<IOBufRefSampleSharedPtr>> StackCountMap;
struct BlockInfo {
int64_t ref{0};
StackCountMap stack_count_map;
};
typedef butil::FlatMap<IOBuf::Block*, BlockInfo> BlockInfoMap;
IOBufProfiler();
~IOBufProfiler() override;
DISALLOW_COPY_AND_ASSIGN(IOBufProfiler);
void Run() override;
// Consume the IOBuf sample in _sample_queue.
void Consume();
// Stop flag of IOBufProfiler.
butil::atomic<bool> _stop;
// IOBuf sample queue.
MPSCQueue<IOBufSample*> _sample_queue;
// Temp buf before saving the file.
butil::IOBuf _disk_buf;
// Combining same samples to make result smaller.
IOBufRefMap _stack_map;
// Record block info.
BlockInfoMap _block_info_map;
Mutex _mutex;
// Sleep when `_sample_queue' is empty.
uint32_t _sleep_ms;
static const uint32_t MIN_SLEEP_MS;
static const uint32_t MAX_SLEEP_MS;
};
bool IsIOBufProfilerEnabled();
bool IsIOBufProfilerSamplable();
void SubmitIOBufSample(IOBuf::Block* block, int64_t ref);
bool IOBufProfilerFlush(const char* filename);
}
#endif //BIGO_BRPC_IOBUF_PROFILER_H