summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/profiler.cpp
blob: 57b775556b20fb9220dd23c0b69a94c8db79831b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
// license:BSD-3-Clause
// copyright-holders:Aaron Giles
/***************************************************************************

    profiler.c

    Functions to manage profiling of MAME execution.

****************************************************************************

    Profiling is scope-based. To start profiling, put a profiler_scope
    object on the stack. To end profiling, just end the scope:

    {
        profiler_scope scope(PROFILER_VIDEO);

        your_work_here();
    }

    the profiler handles a FILO list so calls may be nested.

***************************************************************************/

#include "emu.h"
#include "profiler.h"



//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

struct profile_string
{
	int         type;
	const char *string;
};



//**************************************************************************
//  GLOBAL VARIABLES
//**************************************************************************

profiler_state g_profiler;



//**************************************************************************
//  CONSTANTS
//**************************************************************************

#define TEXT_UPDATE_TIME        0.5



//**************************************************************************
//  DUMMY PROFILER STATE
//**************************************************************************

//-------------------------------------------------
//  dummy_profiler_state - constructor
//-------------------------------------------------

dummy_profiler_state::dummy_profiler_state()
{
}



//**************************************************************************
//  REAL PROFILER STATE
//**************************************************************************

//-------------------------------------------------
//  real_profiler_state - constructor
//-------------------------------------------------

real_profiler_state::real_profiler_state()
{
	memset(m_filo, 0, sizeof(m_filo));
	memset(m_data, 0, sizeof(m_data));
	reset(false);
}



//-------------------------------------------------
//  reset - initializes state
//-------------------------------------------------

void real_profiler_state::reset(bool enabled)
{
	m_text_time = attotime::never;

	if (enabled)
	{
		// we're enabled now
		m_filoptr = m_filo;

		// set up dummy entry
		m_filoptr->start = 0;
		m_filoptr->type = PROFILER_TOTAL;
	}
	else
	{
		// magic value to indicate disabled
		m_filoptr = nullptr;
	}
}



//-------------------------------------------------
//  text - return the current text in an std::string
//-------------------------------------------------

const char *real_profiler_state::text(running_machine &machine)
{
	start(PROFILER_PROFILER);

	// get the current time
	attotime current_time = machine.scheduler().time();

	// we only want to update the text periodically
	if ((m_text_time == attotime::never) || ((current_time - m_text_time).as_double() >= TEXT_UPDATE_TIME))
	{
		update_text(machine);
		m_text_time = current_time;
	}

	stop();
	return m_text.c_str();
}



//-------------------------------------------------
//  update_text - update the current std::string
//-------------------------------------------------

void real_profiler_state::update_text(running_machine &machine)
{
	static const profile_string names[] =
	{
		{ PROFILER_DRC_COMPILE,      "DRC Compilation" },
		{ PROFILER_MEM_REMAP,        "Memory Remapping" },
		{ PROFILER_MEMREAD,          "Memory Read" },
		{ PROFILER_MEMWRITE,         "Memory Write" },
		{ PROFILER_VIDEO,            "Video Update" },
		{ PROFILER_DRAWGFX,          "drawgfx" },
		{ PROFILER_COPYBITMAP,       "copybitmap" },
		{ PROFILER_TILEMAP_DRAW,     "Tilemap Draw" },
		{ PROFILER_TILEMAP_DRAW_ROZ, "Tilemap ROZ Draw" },
		{ PROFILER_TILEMAP_UPDATE,   "Tilemap Update" },
		{ PROFILER_BLIT,             "OSD Blitting" },
		{ PROFILER_SOUND,            "Sound Generation" },
		{ PROFILER_TIMER_CALLBACK,   "Timer Callbacks" },
		{ PROFILER_INPUT,            "Input Processing" },
		{ PROFILER_MOVIE_REC,        "Movie Recording" },
		{ PROFILER_LOGERROR,         "Error Logging" },
		{ PROFILER_EXTRA,            "Unaccounted/Overhead" },
		{ PROFILER_USER1,            "User 1" },
		{ PROFILER_USER2,            "User 2" },
		{ PROFILER_USER3,            "User 3" },
		{ PROFILER_USER4,            "User 4" },
		{ PROFILER_USER5,            "User 5" },
		{ PROFILER_USER6,            "User 6" },
		{ PROFILER_USER7,            "User 7" },
		{ PROFILER_USER8,            "User 8" },
		{ PROFILER_PROFILER,         "Profiler" },
		{ PROFILER_IDLE,             "Idle" }
	};

	// compute the total time for all bits, not including profiler or idle
	UINT64 computed = 0;
	profile_type curtype;
	for (curtype = PROFILER_DEVICE_FIRST; curtype < PROFILER_PROFILER; ++curtype)
		computed += m_data[curtype];

	// save that result in normalize, and continue adding the rest
	UINT64 normalize = computed;
	for ( ; curtype < PROFILER_TOTAL; ++curtype)
		computed += m_data[curtype];

	// this becomes the total; if we end up with 0 for anything, we were just started, so return empty
	UINT64 total = computed;
	m_text.clear();
	if (total == 0 || normalize == 0)
	{
		return;
	}

	// loop over all types and generate the string
	device_iterator iter(machine.root_device());
	for (curtype = PROFILER_DEVICE_FIRST; curtype < PROFILER_TOTAL; ++curtype)
	{
		// determine the accumulated time for this type
		computed = m_data[curtype];

		// if we have non-zero data and we're ready to display, do it
		if (computed != 0)
		{
			// start with the un-normalized percentage
			m_text.append(string_format("%02d%% ", (int)((computed * 100 + total / 2) / total)));

			// followed by the normalized percentage for everything but profiler and idle
			if (curtype < PROFILER_PROFILER)
				m_text.append(string_format("%02d%% ", (int)((computed * 100 + normalize / 2) / normalize)));

			// and then the text
			if (curtype >= PROFILER_DEVICE_FIRST && curtype <= PROFILER_DEVICE_MAX)
				m_text.append(string_format("'%s'", iter.byindex(curtype - PROFILER_DEVICE_FIRST)->tag()));
			else
				for (auto & name : names)
					if (name.type == curtype)
					{
						m_text.append(name.string);
						break;
					}

			// followed by a carriage return
			m_text.append("\n");
		}
	}

	// reset data set to 0
	memset(m_data, 0, sizeof(m_data));
}