blob: cb23c6a49eab9e29716ce570399715e299892eb4 [file] [log] [blame]
// Copyright 2010 Google Inc. All Rights Reserved.
//
// 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: morlovich@google.com (Maksim Orlovich)
#ifndef PAGESPEED_KERNEL_SHAREDMEM_SHARED_MEM_STATISTICS_H_
#define PAGESPEED_KERNEL_SHAREDMEM_SHARED_MEM_STATISTICS_H_
#include <cstddef>
#include "pagespeed/kernel/base/abstract_mutex.h"
#include "pagespeed/kernel/base/abstract_shared_mem.h"
#include "pagespeed/kernel/base/basictypes.h"
#include "pagespeed/kernel/base/scoped_ptr.h"
#include "pagespeed/kernel/base/statistics.h"
#include "pagespeed/kernel/base/statistics_template.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/string_util.h"
namespace net_instaweb {
class FileSystem;
class MessageHandler;
class StatisticsLogger;
class Timer;
// An implementation of Statistics using our shared memory infrastructure.
// These statistics will be shared amongst all processes and threads
// spawned by our host. Note that we will be obtaining a per-variable mutex for
// every read and write to these variables. Since this may be expensive,
// we may need each thread to keep a local cache and infrequently write
// through to this Statistics object. TODO(abliss): actually do this.
//
// Because we must allocate shared memory segments and mutexes before any child
// processes and threads are created, all AddVariable calls must be done in
// the host before it starts forking/threading. Once all variables are added,
// you must call InitVariables(true), and then InitVariables(false) in every
// kid.
//
// If a variable fails to initialize (due to either its mutex or the shared
// memory segment not working), it will not increment in that process (and a
// warning message will be logged). If the variable fails to initialize in the
// process that happens to serve a statistics page, then the variable will show
// up with value -1.
class SharedMemVariable : public MutexedScalar {
public:
SharedMemVariable(StringPiece name, Statistics* stats);
virtual ~SharedMemVariable() {}
virtual StringPiece GetName() const { return name_; }
protected:
virtual AbstractMutex* mutex() const;
virtual int64 GetLockHeld() const;
virtual int64 SetReturningPreviousValueLockHeld(int64 value);
private:
friend class SharedMemStatistics;
friend class SharedMemTimedVariable;
explicit SharedMemVariable(const StringPiece& name);
void AttachTo(AbstractSharedMemSegment* segment_, size_t offset,
MessageHandler* message_handler);
// Called on initialization failure, to make sure it's clear if we
// share some state with parent.
void Reset();
// The name of this variable.
const GoogleString name_;
// Lock protecting us. NULL if for some reason initialization failed.
scoped_ptr<AbstractMutex> mutex_;
// The data...
volatile int64* value_ptr_;
DISALLOW_COPY_AND_ASSIGN(SharedMemVariable);
};
class SharedMemHistogram : public Histogram {
public:
SharedMemHistogram(StringPiece name, Statistics* stats);
virtual ~SharedMemHistogram();
virtual void Add(double value);
virtual void Clear();
virtual int NumBuckets();
// Call the following functions after statistics->Init and before add values.
// EnableNegativeBuckets, SetMinValue and SetMaxValue will
// cause resetting Histogram.
virtual void EnableNegativeBuckets();
// Set the minimum value allowed in histogram.
virtual void SetMinValue(double value);
// Set the upper-bound of value in histogram,
// The value range in histogram is [MinValue, MaxValue) or
// (-MaxValue, MaxValue) if negative buckets are enabled.
virtual void SetMaxValue(double value);
// We rely on NumBuckets to allocate a memory segment for the histogram, so
// this should be called right after AddHistogram() in the ::Initialize
// process. Similarly, all the bounds must be initialized at that point, to
// avoid clearing the histogram as new child processes attach to it.
virtual void SetSuggestedNumBuckets(int i);
// Return the amount of shared memory this Histogram objects needs for its
// use.
size_t AllocationSize(AbstractSharedMem* shm_runtime) {
// Shared memory space should include a mutex, HistogramBody and the storage
// for the actual buckets.
return shm_runtime->SharedMutexSize() + sizeof(HistogramBody) +
sizeof(double) * NumBuckets();
}
protected:
virtual AbstractMutex* lock() {
return mutex_.get();
}
virtual double AverageInternal();
virtual double PercentileInternal(const double perc);
virtual double StandardDeviationInternal();
virtual double CountInternal();
virtual double MaximumInternal();
virtual double MinimumInternal();
virtual double BucketStart(int index);
virtual double BucketCount(int index);
private:
friend class SharedMemStatistics;
void AttachTo(AbstractSharedMemSegment* segment, size_t offset,
MessageHandler* message_handler);
// Returns the width of normal buckets (as in not the two extreme outermost
// buckets which have infinite width).
double BucketWidth();
// Finds a bucket that should contain the given value. Note that this does
// not consider the catcher buckets for out-of-range values.
int FindBucket(double value);
void Init();
void DCheckRanges() const;
void Reset();
void ClearInternal(); // expects mutex_ held, buffer_ != NULL
const GoogleString name_;
scoped_ptr<AbstractMutex> mutex_;
// TODO(fangfei): implement a non-shared-mem histogram.
struct HistogramBody {
// Enable negative values in histogram, false by default.
bool enable_negative_;
// Minimum value allowed in Histogram, 0 by default.
double min_value_;
// Maximum value allowed in Histogram,
// numeric_limits<double>::max() by default.
double max_value_;
// Real minimum value.
double min_;
// Real maximum value.
double max_;
double count_;
double sum_;
double sum_of_squares_;
// Histogram buckets data.
double values_[1];
};
// Number of buckets in this histogram.
int num_buckets_;
HistogramBody* buffer_; // may be NULL if init failed.
DISALLOW_COPY_AND_ASSIGN(SharedMemHistogram);
};
class SharedMemStatistics : public ScalarStatisticsTemplate<
SharedMemVariable, SharedMemHistogram, FakeTimedVariable> {
public:
SharedMemStatistics(int64 logging_interval_ms,
int64 max_logfile_size_kb,
const StringPiece& logging_file, bool logging,
const GoogleString& filename_prefix,
AbstractSharedMem* shm_runtime,
MessageHandler* message_handler, FileSystem* file_system,
Timer* timer);
virtual ~SharedMemStatistics();
// This method initializes or attaches to shared memory. You should call this
// exactly once in each process/thread, after all calls to AddVariables,
// AddHistograms and SetSuggestedNumBuckets (as well as any other histogram
// range configurations) have been done.
//
// The root process (the one that starts all the other child
// threads and processes) must be the first one to make the call, with
// parent = true, with all other calling it with = false.
//
// Returns true if successful.
bool Init(bool parent, MessageHandler* message_handler);
// This should be called from the root process as it is about to exit, when
// no further children are expected to start.
void GlobalCleanup(MessageHandler* message_handler);
// Like above, but can be done after object got cleaned up, by passing
// a saved SegmentName(). Precondition: init must have returned 'true'.
static void GlobalCleanup(AbstractSharedMem* shm_runtime,
const GoogleString& segment_name,
MessageHandler* message_handler);
GoogleString SegmentName() const;
// TODO(sligocki): Rename to statistics_logger().
virtual StatisticsLogger* console_logger() {
return console_logger_.get();
}
protected:
virtual Var* NewVariable(StringPiece name);
virtual UpDown* NewUpDownCounter(StringPiece name);
virtual Hist* NewHistogram(StringPiece name);
private:
// Create mutexes in the segment, with per_var bytes being used,
// counting the mutex, for each variable.
bool InitMutexes(size_t per_var, MessageHandler* message_handler);
friend class SharedMemStatisticsTestBase;
AbstractSharedMem* shm_runtime_;
GoogleString filename_prefix_;
scoped_ptr<AbstractSharedMemSegment> segment_;
bool frozen_;
// TODO(sligocki): Rename.
scoped_ptr<StatisticsLogger> console_logger_;
DISALLOW_COPY_AND_ASSIGN(SharedMemStatistics);
};
} // namespace net_instaweb
#endif // PAGESPEED_KERNEL_SHAREDMEM_SHARED_MEM_STATISTICS_H_