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