trunk/src/emu/profiler.c
| r19439 | r19440 | |
| 76 | 76 | |
| 77 | 77 | |
| 78 | 78 | //************************************************************************** |
| 79 | // CONSTANTS |
| 80 | //************************************************************************** |
| 81 | |
| 82 | #define TEXT_UPDATE_TIME 0.5 |
| 83 | |
| 84 | |
| 85 | |
| 86 | //************************************************************************** |
| 79 | 87 | // DUMMY PROFILER STATE |
| 80 | 88 | //************************************************************************** |
| 81 | 89 | |
| r19439 | r19440 | |
| 98 | 106 | //------------------------------------------------- |
| 99 | 107 | |
| 100 | 108 | real_profiler_state::real_profiler_state() |
| 101 | | : m_enabled(false), |
| 102 | | m_dataready(false), |
| 103 | | m_filoindex(0), |
| 104 | | m_dataindex(0) |
| 105 | 109 | { |
| 106 | 110 | memset(m_filo, 0, sizeof(m_filo)); |
| 107 | 111 | memset(m_data, 0, sizeof(m_data)); |
| 112 | reset(false); |
| 108 | 113 | } |
| 109 | 114 | |
| 110 | 115 | |
| 116 | |
| 111 | 117 | //------------------------------------------------- |
| 112 | | // real_start - mark the beginning of a |
| 113 | | // profiler entry |
| 118 | // reset - initializes state |
| 114 | 119 | //------------------------------------------------- |
| 115 | 120 | |
| 116 | | void real_profiler_state::real_start(profile_type type) |
| 121 | void real_profiler_state::reset(bool enabled) |
| 117 | 122 | { |
| 118 | | osd_ticks_t curticks = get_profile_ticks(); |
| 123 | m_text_time = attotime::never; |
| 119 | 124 | |
| 120 | | // track context switches |
| 121 | | history_data &data = m_data[m_dataindex]; |
| 122 | | if (type >= PROFILER_DEVICE_FIRST && type <= PROFILER_DEVICE_MAX) |
| 123 | | data.context_switches++; |
| 125 | if (enabled) |
| 126 | { |
| 127 | // we're enabled now |
| 128 | m_filoptr = m_filo; |
| 124 | 129 | |
| 125 | | // we're starting a new bucket, begin now |
| 126 | | int index = m_filoindex++; |
| 127 | | filo_entry &entry = m_filo[index]; |
| 128 | | |
| 129 | | // fail if we overflow |
| 130 | | if (index > ARRAY_LENGTH(m_filo)) |
| 131 | | throw emu_fatalerror("Profiler FILO overflow (type = %d)\n", type); |
| 132 | | |
| 133 | | // if we're nested, stop the previous entry |
| 134 | | if (index > 0) |
| 130 | // set up dummy entry |
| 131 | m_filoptr->start = 0; |
| 132 | m_filoptr->type = PROFILER_TOTAL; |
| 133 | } |
| 134 | else |
| 135 | 135 | { |
| 136 | | filo_entry &preventry = m_filo[index - 1]; |
| 137 | | data.duration[preventry.type] += curticks - preventry.start; |
| 136 | // magic value to indicate disabled |
| 137 | m_filoptr = NULL; |
| 138 | 138 | } |
| 139 | | |
| 140 | | // fill in this entry |
| 141 | | entry.type = type; |
| 142 | | entry.start = curticks; |
| 143 | 139 | } |
| 144 | 140 | |
| 145 | 141 | |
| 142 | |
| 146 | 143 | //------------------------------------------------- |
| 147 | | // real_stop - mark the end of a profiler entry |
| 144 | // text - return the current text in an astring |
| 148 | 145 | //------------------------------------------------- |
| 149 | 146 | |
| 150 | | void real_profiler_state::real_stop() |
| 147 | const char *real_profiler_state::text(running_machine &machine) |
| 151 | 148 | { |
| 152 | | osd_ticks_t curticks = get_profile_ticks(); |
| 149 | start(PROFILER_PROFILER); |
| 153 | 150 | |
| 154 | | // we're ending an existing bucket, update the time |
| 155 | | if (m_filoindex > 0) |
| 156 | | { |
| 157 | | int index = --m_filoindex; |
| 158 | | filo_entry &entry = m_filo[index]; |
| 151 | // get the current time |
| 152 | attotime current_time = machine.scheduler().time(); |
| 159 | 153 | |
| 160 | | // account for the time taken |
| 161 | | history_data &data = m_data[m_dataindex]; |
| 162 | | data.duration[entry.type] += curticks - entry.start; |
| 163 | | |
| 164 | | // if we have a previous entry, restart his time now |
| 165 | | if (index != 0) |
| 166 | | { |
| 167 | | filo_entry &preventry = m_filo[index - 1]; |
| 168 | | preventry.start = curticks; |
| 169 | | } |
| 154 | // we only want to update the text periodically |
| 155 | if ((m_text_time == attotime::never) || ((current_time - m_text_time).as_double() >= TEXT_UPDATE_TIME)) |
| 156 | { |
| 157 | update_text(machine); |
| 158 | m_text_time = current_time; |
| 170 | 159 | } |
| 160 | |
| 161 | stop(); |
| 162 | return m_text; |
| 171 | 163 | } |
| 172 | 164 | |
| 173 | 165 | |
| 166 | |
| 174 | 167 | //------------------------------------------------- |
| 175 | | // text - return the current text in an astring |
| 168 | // update_text - update the current astring |
| 176 | 169 | //------------------------------------------------- |
| 177 | 170 | |
| 178 | | const char *real_profiler_state::text(running_machine &machine, astring &string) |
| 171 | void real_profiler_state::update_text(running_machine &machine) |
| 179 | 172 | { |
| 180 | 173 | static const profile_string names[] = |
| 181 | 174 | { |
| r19439 | r19440 | |
| 208 | 201 | { PROFILER_IDLE, "Idle" } |
| 209 | 202 | }; |
| 210 | 203 | |
| 211 | | g_profiler.start(PROFILER_PROFILER); |
| 212 | | |
| 213 | 204 | // compute the total time for all bits, not including profiler or idle |
| 214 | 205 | UINT64 computed = 0; |
| 215 | 206 | profile_type curtype; |
| 216 | 207 | for (curtype = PROFILER_DEVICE_FIRST; curtype < PROFILER_PROFILER; curtype++) |
| 217 | | for (int curmem = 0; curmem < ARRAY_LENGTH(m_data); curmem++) |
| 218 | | computed += m_data[curmem].duration[curtype]; |
| 208 | computed += m_data[curtype]; |
| 219 | 209 | |
| 220 | 210 | // save that result in normalize, and continue adding the rest |
| 221 | 211 | UINT64 normalize = computed; |
| 222 | 212 | for ( ; curtype < PROFILER_TOTAL; curtype++) |
| 223 | | for (int curmem = 0; curmem < ARRAY_LENGTH(m_data); curmem++) |
| 224 | | computed += m_data[curmem].duration[curtype]; |
| 213 | computed += m_data[curtype]; |
| 225 | 214 | |
| 226 | 215 | // this becomes the total; if we end up with 0 for anything, we were just started, so return empty |
| 227 | 216 | UINT64 total = computed; |
| 228 | | string.reset(); |
| 217 | m_text.reset(); |
| 229 | 218 | if (total == 0 || normalize == 0) |
| 230 | 219 | { |
| 231 | | g_profiler.stop(); |
| 232 | | return string; |
| 220 | return; |
| 233 | 221 | } |
| 234 | 222 | |
| 235 | 223 | // loop over all types and generate the string |
| r19439 | r19440 | |
| 237 | 225 | for (curtype = PROFILER_DEVICE_FIRST; curtype < PROFILER_TOTAL; curtype++) |
| 238 | 226 | { |
| 239 | 227 | // determine the accumulated time for this type |
| 240 | | computed = 0; |
| 241 | | for (int curmem = 0; curmem < ARRAY_LENGTH(m_data); curmem++) |
| 242 | | computed += m_data[curmem].duration[curtype]; |
| 228 | computed = m_data[curtype]; |
| 243 | 229 | |
| 244 | 230 | // if we have non-zero data and we're ready to display, do it |
| 245 | | if (m_dataready && computed != 0) |
| 231 | if (computed != 0) |
| 246 | 232 | { |
| 247 | 233 | // start with the un-normalized percentage |
| 248 | | string.catprintf("%02d%% ", (int)((computed * 100 + total/2) / total)); |
| 234 | m_text.catprintf("%02d%% ", (int)((computed * 100 + total/2) / total)); |
| 249 | 235 | |
| 250 | 236 | // followed by the normalized percentage for everything but profiler and idle |
| 251 | 237 | if (curtype < PROFILER_PROFILER) |
| 252 | | string.catprintf("%02d%% ", (int)((computed * 100 + normalize/2) / normalize)); |
| 238 | m_text.catprintf("%02d%% ", (int)((computed * 100 + normalize/2) / normalize)); |
| 253 | 239 | |
| 254 | 240 | // and then the text |
| 255 | 241 | if (curtype >= PROFILER_DEVICE_FIRST && curtype <= PROFILER_DEVICE_MAX) |
| 256 | | string.catprintf("'%s'", iter.byindex(curtype - PROFILER_DEVICE_FIRST)->tag()); |
| 242 | m_text.catprintf("'%s'", iter.byindex(curtype - PROFILER_DEVICE_FIRST)->tag()); |
| 257 | 243 | else |
| 258 | 244 | for (int nameindex = 0; nameindex < ARRAY_LENGTH(names); nameindex++) |
| 259 | 245 | if (names[nameindex].type == curtype) |
| 260 | 246 | { |
| 261 | | string.cat(names[nameindex].string); |
| 247 | m_text.cat(names[nameindex].string); |
| 262 | 248 | break; |
| 263 | 249 | } |
| 264 | 250 | |
| 265 | 251 | // followed by a carriage return |
| 266 | | string.cat("\n"); |
| 252 | m_text.cat("\n"); |
| 267 | 253 | } |
| 268 | 254 | } |
| 269 | 255 | |
| 270 | | // followed by context switches |
| 271 | | if (m_dataready) |
| 272 | | { |
| 273 | | int switches = 0; |
| 274 | | for (int curmem = 0; curmem < ARRAY_LENGTH(m_data); curmem++) |
| 275 | | switches += m_data[curmem].context_switches; |
| 276 | | string.catprintf("%d CPU switches\n", switches / (int) ARRAY_LENGTH(m_data)); |
| 277 | | } |
| 278 | | |
| 279 | | // advance to the next dataset and reset it to 0 |
| 280 | | m_dataindex = (m_dataindex + 1) % ARRAY_LENGTH(m_data); |
| 281 | | memset(&m_data[m_dataindex], 0, sizeof(m_data[m_dataindex])); |
| 282 | | |
| 283 | | // we are ready once we have wrapped around |
| 284 | | if (m_dataindex == 0) |
| 285 | | m_dataready = true; |
| 286 | | |
| 287 | | g_profiler.stop(); |
| 288 | | return string; |
| 256 | // reset data set to 0 |
| 257 | memset(m_data, 0, sizeof(m_data)); |
| 289 | 258 | } |
trunk/src/emu/profiler.h
| r19439 | r19440 | |
| 55 | 55 | #ifndef __PROFILER_H__ |
| 56 | 56 | #define __PROFILER_H__ |
| 57 | 57 | |
| 58 | #include "attotime.h" |
| 58 | 59 | |
| 59 | 60 | |
| 61 | |
| 60 | 62 | //************************************************************************** |
| 61 | 63 | // CONSTANTS |
| 62 | 64 | //************************************************************************** |
| r19439 | r19440 | |
| 111 | 113 | |
| 112 | 114 | class real_profiler_state |
| 113 | 115 | { |
| 114 | | friend class profile_scope; |
| 115 | | |
| 116 | 116 | public: |
| 117 | 117 | // construction/destruction |
| 118 | 118 | real_profiler_state(); |
| 119 | 119 | |
| 120 | 120 | // getters |
| 121 | | bool enabled() const { return m_enabled; } |
| 122 | | const char *text(running_machine &machine, astring &string); |
| 121 | bool enabled() const { return m_filoptr != NULL; } |
| 122 | const char *text(running_machine &machine); |
| 123 | 123 | |
| 124 | 124 | // enable/disable |
| 125 | 125 | void enable(bool state = true) |
| 126 | 126 | { |
| 127 | | if (state != m_enabled) |
| 127 | if (state != enabled()) |
| 128 | 128 | { |
| 129 | | m_enabled = state; |
| 130 | | if (m_enabled) |
| 131 | | { |
| 132 | | m_dataready = false; |
| 133 | | m_filoindex = m_dataindex = 0; |
| 134 | | } |
| 129 | reset(state); |
| 135 | 130 | } |
| 136 | 131 | } |
| 137 | 132 | |
| 138 | 133 | // start/stop |
| 139 | | void start(profile_type type) { if (m_enabled) real_start(type); } |
| 140 | | void stop() { if (m_enabled) real_stop(); } |
| 134 | void start(profile_type type) { if (enabled()) real_start(type); } |
| 135 | void stop() { if (enabled()) real_stop(); } |
| 141 | 136 | |
| 142 | 137 | private: |
| 143 | | void real_start(profile_type type); |
| 144 | | void real_stop(); |
| 138 | void reset(bool enabled); |
| 139 | void update_text(running_machine &machine); |
| 145 | 140 | |
| 141 | //------------------------------------------------- |
| 142 | // real_start - mark the beginning of a |
| 143 | // profiler entry |
| 144 | //------------------------------------------------- |
| 145 | ATTR_FORCE_INLINE void real_start(profile_type type) |
| 146 | { |
| 147 | // fail if we overflow |
| 148 | if (m_filoptr >= &m_filo[ARRAY_LENGTH(m_filo) - 1]) |
| 149 | throw emu_fatalerror("Profiler FILO overflow (type = %d)\n", type); |
| 150 | |
| 151 | // get current tick count |
| 152 | osd_ticks_t curticks = get_profile_ticks(); |
| 153 | |
| 154 | // update previous entry |
| 155 | m_data[m_filoptr->type] += curticks - m_filoptr->start; |
| 156 | |
| 157 | // move to next entry |
| 158 | m_filoptr++; |
| 159 | |
| 160 | // fill in this entry |
| 161 | m_filoptr->type = type; |
| 162 | m_filoptr->start = curticks; |
| 163 | } |
| 164 | |
| 165 | //------------------------------------------------- |
| 166 | // real_stop - mark the end of a profiler entry |
| 167 | //------------------------------------------------- |
| 168 | ATTR_FORCE_INLINE void real_stop() |
| 169 | { |
| 170 | // degenerate scenario |
| 171 | if (UNEXPECTED(m_filoptr <= m_filo)) |
| 172 | return; |
| 173 | |
| 174 | // get current tick count |
| 175 | osd_ticks_t curticks = get_profile_ticks(); |
| 176 | |
| 177 | // account for the time taken |
| 178 | m_data[m_filoptr->type] += curticks - m_filoptr->start; |
| 179 | |
| 180 | // move back an entry |
| 181 | m_filoptr--; |
| 182 | |
| 183 | // reset previous entry start time |
| 184 | m_filoptr->start = curticks; |
| 185 | } |
| 186 | |
| 146 | 187 | // an entry in the FILO |
| 147 | 188 | struct filo_entry |
| 148 | 189 | { |
| r19439 | r19440 | |
| 150 | 191 | osd_ticks_t start; // start time |
| 151 | 192 | }; |
| 152 | 193 | |
| 153 | | // item in the array of recent states |
| 154 | | struct history_data |
| 155 | | { |
| 156 | | UINT32 context_switches; // number of context switches seen |
| 157 | | osd_ticks_t duration[PROFILER_TOTAL]; // duration spent in each entry |
| 158 | | }; |
| 159 | | |
| 160 | 194 | // internal state |
| 161 | | bool m_enabled; // are we enabled? |
| 162 | | bool m_dataready; // are we to display the data yet? |
| 163 | | UINT8 m_filoindex; // current FILO index |
| 164 | | UINT8 m_dataindex; // current data index |
| 165 | | filo_entry m_filo[16]; // array of FILO entries |
| 166 | | history_data m_data[16]; // array of data |
| 195 | filo_entry * m_filoptr; // current FILO index |
| 196 | astring m_text; // profiler text |
| 197 | attotime m_text_time; // profiler text last update |
| 198 | filo_entry m_filo[32]; // array of FILO entries |
| 199 | osd_ticks_t m_data[PROFILER_TOTAL + 1]; // array of data |
| 167 | 200 | }; |
| 168 | 201 | |
| 169 | 202 | |
| r19439 | r19440 | |
| 177 | 210 | |
| 178 | 211 | // getters |
| 179 | 212 | bool enabled() const { return false; } |
| 180 | | const char *text(running_machine &machine, astring &string) { return string.cpy(""); } |
| 213 | const char *text(running_machine &machine) { return ""; } |
| 181 | 214 | |
| 182 | 215 | // enable/disable |
| 183 | 216 | void enable(bool state = true) { } |