1/* 2 * Copyright (c) 2011 Apple Inc. All rights reserved. 3 * 4 * @APPLE_APACHE_LICENSE_HEADER_START@ 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 * 18 * @APPLE_APACHE_LICENSE_HEADER_END@ 19 */ 20/* 21 Statistics.h 22 Copyright (c) 2004-2011 Apple Inc. All rights reserved. 23 */ 24 25#pragma once 26#ifndef __AUTO_STATISTICS__ 27#define __AUTO_STATISTICS__ 28 29 30#include "Definitions.h" 31#include <mach/thread_act.h> 32#include <assert.h> 33 34 35namespace Auto { 36 37 //----- Timing -----// 38 39 // The Timer template provides a generic timer model. The template parameter can wrap different system timers. 40 // The TimeDataSource must provide: 41 // uint64_t current_time(void) - return the current time, using arbitrary time units 42 // uint64_t microseconds_duration(uint64_t start, uint64_t end) - compute the duration in microseconds between start and end 43 44 template <class TimeDataSource> class Timer { 45 TimeDataSource _timeDataSource; // The timer data source. 46 volatile uint64_t _start; // The absolute time when the timer was last started (0 if the timer is not running) 47 volatile int64_t _accumulated; // The total accumulated time, in microseconds 48 char _description[8]; // A string buffer used to return a textual representation of the recorded time 49 50 public: 51 Timer() : _start(0), _accumulated(0) {} 52 53 // Test whether the timer is currently running. 54 inline boolean_t timer_running() const { return _start != 0; } 55 56 // Start the timer. 57 void start() { assert(!timer_running()); _start = _timeDataSource.current_time(); } 58 59 // Stop the timer. The duration since the last call to start is added to the accumulated time. 60 void stop() { assert(timer_running()); add_time(_timeDataSource.microseconds_duration(_start, _timeDataSource.current_time())); _start = 0; } 61 62 // Clear the accumulated time. 63 void reset() { assert(!timer_running()); _accumulated = 0; } 64 65 // Add time to the timer. duration is in microseconds. 66 void add_time(usword_t duration) { OSAtomicAdd64(duration, &_accumulated); } 67 68 // Add the accumulated time from another timer to this timer. 69 void add_time(Timer &other) { OSAtomicAdd64(other.microseconds(), &_accumulated); } 70 71 // Return the accumulated time in microseconds. 72 int64_t microseconds() const { assert(!timer_running()); return _accumulated; } 73 74 // Returns the current elapsed time on a running timer. Does not stop the timer. 75 // Returns zero if the timer is running. 76 int64_t elapsed_microseconds() { 77 int64_t start = _start; 78 return start != 0 ? _timeDataSource.microseconds_duration(start, _timeDataSource.current_time()) : 0; 79 } 80 81 // Return a formatted textual representation of the accumulated time. 82 // The returned buffer lives in the object being queried. 83 const char *time_string() { 84 int64_t timeval = microseconds(); 85 if (timeval < 999) { 86 snprintf(_description, sizeof(_description), "%4.3g us", (float)timeval); 87 } else if (timeval < 999999) { 88 snprintf(_description, sizeof(_description), "%4.3g ms", (float)timeval/1000); 89 } else { 90 snprintf(_description, sizeof(_description), "%4.3g s", (float)timeval/1000/1000); 91 } 92 return _description; 93 } 94 }; 95 96#if 0 97 // A timer data source that measures time based on the user cpu time charged to the calling thread. 98 // This data source is relatively expensive in that it requires a kernel call. 99 class UserCPUTimeDataSource { 100 public: 101 inline uint64_t current_time(void) { 102 thread_basic_info_data_t myinfo; 103 unsigned int count = sizeof(myinfo); 104 thread_info(pthread_mach_thread_np(pthread_self()), THREAD_BASIC_INFO, (thread_info_t)&myinfo, &count); 105 return (int64_t)myinfo.user_time.seconds*1000000 + (int64_t)myinfo.user_time.microseconds; 106 } 107 inline uint64_t microseconds_duration(uint64_t start, uint64_t end) { return end - start; } 108 }; 109 typedef Timer<UserCPUTimeDataSource> UserCPUTimer; 110#endif 111 112 // A timer data source that measures elapsed wall clock time. This timer has very little cost to use (no system call). 113 class WallClockTimeDataSource { 114 inline static mach_timebase_info_data_t &cached_timebase() { 115 static mach_timebase_info_data_t _timebase; 116 if (_timebase.denom == 0) { 117 mach_timebase_info(&_timebase); 118 _timebase.denom *= 1000; // we're using microseconds instead of nanoseconds 119 } 120 return _timebase; 121 } 122 123 public: 124 uint64_t current_time(void) { return mach_absolute_time(); } 125 inline uint64_t microseconds_duration(uint64_t start, uint64_t end) { 126 mach_timebase_info_data_t &timebase = cached_timebase(); 127 return (end - start) * timebase.numer / timebase.denom; 128 } 129 130 WallClockTimeDataSource() { } 131 }; 132 typedef Timer<WallClockTimeDataSource> WallClockTimer; 133 134 135 // CollectionTimer wraps up the timing statistics that are collected during a heap collection. 136 class CollectionTimer { 137 public: 138 // Typedefs to permit changing the underlying clock for the measurement 139 typedef WallClockTimer TotalCollectionTimer; 140 typedef WallClockTimer ScanTimer; 141 142 private: 143 TotalCollectionTimer _total_time; // timer that runs from the beginning to end of the heap collection 144 ScanTimer _scan_timer; // timer that accumulates all the time threads spend scanning 145 boolean_t _scan_timer_enabled; 146 147 public: 148 CollectionTimer() : _scan_timer_enabled(false) {} 149 150 // Accessors for the particular timers. 151 inline TotalCollectionTimer &total_time() { return _total_time; } 152 inline ScanTimer &scan_timer() { assert(_scan_timer_enabled); return _scan_timer; } 153 154 inline void enable_scan_timer() { _scan_timer_enabled = true; } 155 inline boolean_t scan_timer_enabled() { return _scan_timer_enabled; } 156 }; 157 158 159 //----- Statistics -----// 160 161 class Statistics { 162 // Number of allocated Large/Subzone blocks. Includes blocks on the thread local allocation cache. 163 // (ie "allocated" is based on subzone perspective). 164 volatile uint64_t _count; 165 166 // total # bytes represented by blocks included in _count 167 volatile uint64_t _size; 168 169 // per heap collection statistics 170 volatile uint64_t _blocks_scanned; 171 volatile uint64_t _bytes_scanned; 172 173 WallClockTimer _idle_timer; // measures the interval between heap collections 174 volatile int64_t _should_collect_interval_start; // records the last time Zone::should_check() ran, to throttle requests 175 176//#define MEASURE_TLC_STATS 177#ifdef MEASURE_TLC_STATS 178 // These are all block counts, and don't include larges. 179 volatile uint64_t _local_allocations; // blocks allocated thread local 180 volatile uint64_t _global_allocations; // blocks allocated not thread local 181 volatile uint64_t _escaped; // count of blocks that transitioned local->global 182 volatile uint64_t _local_collected; // count of local garbage blocks 183 volatile uint64_t _global_collected; // count of global garbage blocks (excluding Large blocks) 184 volatile uint64_t _recycled; // count of locally recovered blocks 185 volatile uint64_t _global_freed; // count of local garbage which was passed to global collector to finalize/free 186#endif 187 188 public: 189 // 190 // Constructor 191 // 192 Statistics() { bzero(this, sizeof(Statistics)); } 193 194 // 195 // Reset before starting a heap collection 196 // 197 inline void reset_for_heap_collection() { 198 _blocks_scanned = 0; 199 _bytes_scanned = 0; 200 } 201 202 // 203 // Accessors 204 // 205 inline uint64_t count() const { return _count; } 206 inline uint64_t size() const { return _size; } 207 WallClockTimer &idle_timer() { return _idle_timer; } 208 inline uint64_t blocks_scanned() const { return _blocks_scanned; } 209 inline uint64_t bytes_scanned() const { return _bytes_scanned; } 210 inline volatile int64_t *last_should_collect_time() { return &_should_collect_interval_start;} 211#ifdef MEASURE_TLC_STATS 212 inline void print_tlc_stats() { malloc_printf("allocations - local: %ld, global: %ld. Escaped: %ld. collected - local: %ld, global: %ld. Recovered - local: %ld, global: %ld\n", _local_allocations, _global_allocations, _escaped, _local_collected, _global_collected, _recycled, _global_freed); } 213#endif 214 215 // 216 // Accumulators 217 // 218 inline void add_count(int64_t n) { OSAtomicAdd64(n, (volatile int64_t *)&_count); } 219 inline void add_size(int64_t size) { OSAtomicAdd64(size, (volatile int64_t *)&_size); } 220 inline void add_blocks_scanned(int64_t count) { OSAtomicAdd64(count, (volatile int64_t *)&_blocks_scanned); } 221 inline void add_bytes_scanned(int64_t count) { OSAtomicAdd64(count, (volatile int64_t *)&_bytes_scanned); } 222#ifdef MEASURE_TLC_STATS 223 inline void add_local_allocations(int64_t n) { OSAtomicAdd64(n, (volatile int64_t *)&_local_allocations); } 224 inline void add_global_allocations(int64_t n) { OSAtomicAdd64(n, (volatile int64_t *)&_global_allocations); } 225 inline void add_escaped(int64_t n) { OSAtomicAdd64(n, (volatile int64_t *)&_escaped); } 226 inline void add_local_collected(int64_t n) { OSAtomicAdd64(n, (volatile int64_t *)&_local_collected); } 227 inline void add_global_collected(int64_t n) { OSAtomicAdd64(n, (volatile int64_t *)&_global_collected); } 228 inline void add_recycled(int64_t n) { OSAtomicAdd64(n, (volatile int64_t *)&_recycled); } 229 inline void add_global_freed(int64_t n) { OSAtomicAdd64(n, (volatile int64_t *)&_global_freed); } 230#endif 231 }; 232}; 233 234#endif // __AUTO_STATISTICS__ 235 236