core/logic/Profiler.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Wed Apr 23 21:40:50 2014 -0400 (2014-04-23)
changeset 4332 75c299312bec
parent 4068 5a6151072b18
permissions -rw-r--r--
Update CS:GO gamedata.
dvander@1908
     1
/**
dvander@4068
     2
 * vim: set ts=4 sw=4 tw=99 noet :
dvander@1908
     3
 * =============================================================================
dvander@1908
     4
 * SourceMod
ds@1947
     5
 * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
dvander@1908
     6
 * =============================================================================
dvander@1908
     7
 *
dvander@1908
     8
 * This program is free software; you can redistribute it and/or modify it under
dvander@1908
     9
 * the terms of the GNU General Public License, version 3.0, as published by the
dvander@1908
    10
 * Free Software Foundation.
dvander@1908
    11
 * 
dvander@1908
    12
 * This program is distributed in the hope that it will be useful, but WITHOUT
dvander@1908
    13
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
dvander@1908
    14
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
dvander@1908
    15
 * details.
dvander@1908
    16
 *
dvander@1908
    17
 * You should have received a copy of the GNU General Public License along with
dvander@1908
    18
 * this program.  If not, see <http://www.gnu.org/licenses/>.
dvander@1908
    19
 *
dvander@1908
    20
 * As a special exception, AlliedModders LLC gives you permission to link the
dvander@1908
    21
 * code of this program (as well as its derivative works) to "Half-Life 2," the
dvander@1908
    22
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
dvander@1908
    23
 * by the Valve Corporation.  You must obey the GNU General Public License in
dvander@1908
    24
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
dvander@1908
    25
 * this exception to all derivative works.  AlliedModders LLC defines further
dvander@1908
    26
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
dvander@1908
    27
 * or <http://www.sourcemod.net/license.php>.
dvander@1908
    28
 *
dvander@1908
    29
 * Version: $Id$
dvander@1908
    30
 */
dvander@1908
    31
dvander@1908
    32
#include "Profiler.h"
dvander@2728
    33
#include <ISourceMod.h>
dvander@1911
    34
#if defined PLATFORM_POSIX
dvander@1911
    35
#include <sys/time.h>
dvander@1911
    36
#include <time.h>
dvander@1911
    37
#endif
dvander@2728
    38
#include <IPluginSys.h>
dvander@3005
    39
#include "stringutil.h"
dvander@1908
    40
dvander@1908
    41
ProfileEngine g_Profiler;
dvander@1908
    42
IProfiler *sm_profiler = &g_Profiler;
dvander@1908
    43
dvander@1908
    44
#if defined PLATFORM_WINDOWS
dvander@1908
    45
double WINDOWS_PERFORMANCE_FREQUENCY;
dvander@1908
    46
#endif
dvander@1908
    47
dvander@1908
    48
class EmptyProfiler : public IProfiler
dvander@1908
    49
{
dvander@1908
    50
public:
dvander@1908
    51
	void OnNativeBegin(IPluginContext *pContext, sp_native_t *native)
dvander@1908
    52
	{
dvander@1908
    53
	}
dvander@1908
    54
	void OnNativeEnd()
dvander@1908
    55
	{
dvander@1908
    56
	}
dvander@1908
    57
	void OnFunctionBegin(IPluginContext *pContext, const char *name)
dvander@1908
    58
	{
dvander@1908
    59
	}
dvander@1908
    60
	void OnFunctionEnd()
dvander@1908
    61
	{
dvander@1908
    62
	}
dvander@1908
    63
	int OnCallbackBegin(IPluginContext *pContext, sp_public_t *pubfunc)
dvander@1908
    64
	{
dvander@1908
    65
		return 0;
dvander@1908
    66
	}
dvander@1908
    67
	void OnCallbackEnd(int serial)
dvander@1908
    68
	{
dvander@1908
    69
	}
dvander@1908
    70
} s_EmptyProfiler;
dvander@1908
    71
dvander@1908
    72
inline void InitProfPoint(prof_point_t &pt)
dvander@1908
    73
{
dvander@1908
    74
#if defined PLATFORM_WINDOWS
dvander@1908
    75
	QueryPerformanceCounter(&pt.value);
dvander@1908
    76
#elif defined PLATFORM_POSIX
dvander@1908
    77
	gettimeofday(&pt.value, NULL);
dvander@1908
    78
#endif
dvander@1908
    79
	pt.is_set = true;
dvander@1908
    80
}
dvander@1908
    81
dvander@1908
    82
ProfileEngine::ProfileEngine()
dvander@1908
    83
{
dvander@1908
    84
	m_serial = 0;
dvander@1908
    85
dvander@1908
    86
#if defined PLATFORM_WINDOWS
dvander@1908
    87
	LARGE_INTEGER pf;
dvander@1908
    88
dvander@1908
    89
	if (QueryPerformanceFrequency(&pf))
dvander@1908
    90
	{
dvander@1908
    91
		WINDOWS_PERFORMANCE_FREQUENCY = 1.0 / (double)(pf.QuadPart);
dvander@1908
    92
	}
dvander@1908
    93
	else
dvander@1908
    94
	{
dvander@1908
    95
		WINDOWS_PERFORMANCE_FREQUENCY = -1.0;
dvander@1908
    96
	}
dvander@1908
    97
#endif
dvander@1908
    98
dvander@1908
    99
	if (IsEnabled())
dvander@1908
   100
	{
dvander@1908
   101
		InitProfPoint(m_ProfStart);
dvander@1908
   102
	}
dvander@1908
   103
	else
dvander@1908
   104
	{
dvander@1908
   105
		sm_profiler = &s_EmptyProfiler;
dvander@1908
   106
	}
dvander@1908
   107
}
dvander@1908
   108
dvander@1908
   109
bool ProfileEngine::IsEnabled()
dvander@1908
   110
{
dvander@1908
   111
#if defined PLATFORM_WINDOWS
dvander@1908
   112
	return (WINDOWS_PERFORMANCE_FREQUENCY > 0.0);
dvander@1908
   113
#elif defined PLATFORM_POSIX
dvander@1908
   114
	return true;
dvander@1908
   115
#endif
dvander@1908
   116
}
dvander@1908
   117
dvander@1908
   118
inline double DiffProfPoints(const prof_point_t &start, const prof_point_t &end)
dvander@1908
   119
{
dvander@1908
   120
	double seconds;
dvander@1908
   121
dvander@1908
   122
#if defined PLATFORM_WINDOWS
dvander@1908
   123
	LONGLONG diff;
dvander@1908
   124
	
dvander@1908
   125
	diff = end.value.QuadPart - start.value.QuadPart;
dvander@1908
   126
	seconds = diff * WINDOWS_PERFORMANCE_FREQUENCY;
dvander@1908
   127
#elif defined PLATFORM_POSIX
dvander@1908
   128
	seconds = (double)(end.value.tv_sec - start.value.tv_sec);
dvander@1908
   129
dvander@1911
   130
	if (start.value.tv_usec > end.value.tv_usec)
dvander@1908
   131
	{
dvander@1911
   132
		seconds -= 1.0;
dvander@1911
   133
		seconds += (double)(1000000 - (start.value.tv_usec - end.value.tv_usec)) / 1000000.0;
dvander@1908
   134
	}
dvander@1908
   135
	else
dvander@1908
   136
	{
dvander@1911
   137
		seconds += (double)(end.value.tv_usec - start.value.tv_usec) / 1000000.0;
dvander@1908
   138
	}
dvander@1908
   139
#endif
dvander@1908
   140
dvander@1908
   141
	return seconds;
dvander@1908
   142
}
dvander@1908
   143
dvander@1908
   144
inline double CalcAtomTime(const prof_atom_t &atom)
dvander@1908
   145
{
dvander@1908
   146
	if (!atom.end.is_set)
dvander@1908
   147
	{
dvander@1908
   148
		return atom.base_time;
dvander@1908
   149
	}
dvander@1908
   150
dvander@1908
   151
	return atom.base_time + DiffProfPoints(atom.start, atom.end);
dvander@1908
   152
}
dvander@1908
   153
dvander@1908
   154
void ProfileEngine::OnNativeBegin(IPluginContext *pContext, sp_native_t *native)
dvander@1908
   155
{
dvander@1908
   156
	PushProfileStack(pContext, SP_PROF_NATIVES, native->name);
dvander@1908
   157
}
dvander@1908
   158
dvander@1908
   159
void ProfileEngine::OnNativeEnd()
dvander@1908
   160
{
dvander@1908
   161
	assert(!m_AtomStack.empty());
dvander@1908
   162
	assert(m_AtomStack.front().atom_type == SP_PROF_NATIVES);
dvander@1908
   163
dvander@1908
   164
	PopProfileStack(&m_Natives);
dvander@1908
   165
}
dvander@1908
   166
dvander@1908
   167
void ProfileEngine::OnFunctionBegin(IPluginContext *pContext, const char *name)
dvander@1908
   168
{
dvander@1908
   169
	PushProfileStack(pContext, SP_PROF_FUNCTIONS, name);
dvander@1908
   170
}
dvander@1908
   171
dvander@1908
   172
void ProfileEngine::OnFunctionEnd()
dvander@1908
   173
{
dvander@1908
   174
	assert(!m_AtomStack.empty());
dvander@1908
   175
	assert(m_AtomStack.front().atom_type == SP_PROF_FUNCTIONS);
dvander@1908
   176
dvander@1908
   177
	PopProfileStack(&m_Functions);
dvander@1908
   178
}
dvander@1908
   179
dvander@1908
   180
int ProfileEngine::OnCallbackBegin(IPluginContext *pContext, sp_public_t *pubfunc)
dvander@1908
   181
{
dvander@1908
   182
	PushProfileStack(pContext, SP_PROF_CALLBACKS, pubfunc->name);
dvander@1908
   183
dvander@1908
   184
	return m_serial;
dvander@1908
   185
}
dvander@1908
   186
dvander@1908
   187
void ProfileEngine::OnCallbackEnd(int serial)
dvander@1908
   188
{
dvander@1908
   189
	assert(!m_AtomStack.empty());
dvander@1908
   190
dvander@1908
   191
	/**
dvander@1908
   192
	 * Account for the situation where the JIT discards the 
dvander@1908
   193
	 * stack because there was an RTE of sorts.
dvander@1908
   194
	 */
dvander@1908
   195
	if (m_AtomStack.front().atom_type != SP_PROF_CALLBACKS
dvander@1908
   196
		&& m_AtomStack.front().atom_serial != serial)
dvander@1908
   197
	{
dvander@1908
   198
		prof_atom_t atom;
dvander@1908
   199
		double total_time;
dvander@1908
   200
dvander@1908
   201
		/* There was an error, and we need to discard things. */
dvander@1908
   202
		total_time = 0.0;
dvander@1908
   203
		while (!m_AtomStack.empty() 
dvander@1908
   204
			&& m_AtomStack.front().atom_type != SP_PROF_CALLBACKS 
dvander@1908
   205
			&& m_AtomStack.front().atom_serial != serial)
dvander@1908
   206
		{
dvander@1908
   207
			total_time += CalcAtomTime(m_AtomStack.front());
dvander@1908
   208
			m_AtomStack.pop();
dvander@1908
   209
		}
dvander@1908
   210
dvander@1908
   211
		/**
dvander@1908
   212
		 * Now we can end and discard ourselves, without saving the data.
dvander@1908
   213
		 * Since this data is all erroneous anyway, we don't care if it's 
dvander@1908
   214
		 * not totally accurate. 
dvander@1908
   215
		 */
dvander@1908
   216
dvander@1908
   217
		assert(!m_AtomStack.empty());
dvander@1908
   218
		atom = m_AtomStack.front();
dvander@1908
   219
		m_AtomStack.pop();
dvander@1908
   220
dvander@1908
   221
		/* Note: We don't need to resume ourselves because end is set by Pause(). */
dvander@1908
   222
		total_time += CalcAtomTime(atom);
dvander@1908
   223
dvander@1908
   224
		ResumeParent(total_time);
dvander@1908
   225
		return;
dvander@1908
   226
	}
dvander@1908
   227
dvander@1908
   228
	PopProfileStack(&m_Callbacks);
dvander@1908
   229
}
dvander@1908
   230
dvander@1908
   231
void ProfileEngine::PushProfileStack(IPluginContext *ctx, int type, const char *name)
dvander@1908
   232
{
dvander@1908
   233
	prof_atom_t atom;
dvander@1908
   234
dvander@1908
   235
	PauseParent();
dvander@1908
   236
dvander@1908
   237
	atom.atom_type = type;
dvander@1908
   238
	atom.base_time = 0.0;
dvander@1908
   239
	atom.ctx = ctx->GetContext();
dvander@1908
   240
	atom.name = name;
dvander@1908
   241
	atom.end.is_set = false;
dvander@1908
   242
dvander@1908
   243
	if (type == SP_PROF_CALLBACKS)
dvander@1908
   244
	{
dvander@1908
   245
		atom.atom_serial = ++m_serial;
dvander@1908
   246
	}
dvander@1908
   247
	else
dvander@1908
   248
	{
dvander@1908
   249
		atom.atom_serial = 0;
dvander@1908
   250
	}
dvander@1908
   251
dvander@1908
   252
	m_AtomStack.push(atom);
dvander@1908
   253
dvander@1908
   254
	/* Note: We do this after because the stack could grow and skew results */
dvander@1908
   255
	InitProfPoint(m_AtomStack.front().start);
dvander@1908
   256
}
dvander@1908
   257
dvander@1908
   258
void ProfileEngine::PopProfileStack(ProfileReport *reporter)
dvander@1908
   259
{
dvander@1908
   260
	double total_time;
dvander@1908
   261
dvander@1908
   262
	prof_atom_t &atom = m_AtomStack.front();
dvander@1908
   263
dvander@1908
   264
	/* We're okay to cache our used time. */
dvander@1908
   265
	InitProfPoint(atom.end);
dvander@1908
   266
	total_time = CalcAtomTime(atom);
dvander@1908
   267
dvander@1908
   268
	/* Now it's time to save this! This may do a lot of computations which 
dvander@1908
   269
	 * is why we've cached the time beforehand.
dvander@1908
   270
	 */
dvander@1908
   271
	reporter->SaveAtom(atom);
dvander@1908
   272
	m_AtomStack.pop();
dvander@1908
   273
dvander@1908
   274
	/* Finally, tell our parent how much time we used. */
dvander@1908
   275
	ResumeParent(total_time);
dvander@1908
   276
}
dvander@1908
   277
dvander@1908
   278
void ProfileEngine::PauseParent()
dvander@1908
   279
{
dvander@1908
   280
	if (m_AtomStack.empty())
dvander@1908
   281
	{
dvander@1908
   282
		return;
dvander@1908
   283
	}
dvander@1908
   284
dvander@1908
   285
	InitProfPoint(m_AtomStack.front().end);
dvander@1908
   286
}
dvander@1908
   287
dvander@1908
   288
void ProfileEngine::ResumeParent(double addTime)
dvander@1908
   289
{
dvander@1908
   290
	if (m_AtomStack.empty())
dvander@1908
   291
	{
dvander@1908
   292
		return;
dvander@1908
   293
	}
dvander@1908
   294
dvander@1908
   295
	prof_atom_t &atom = m_AtomStack.front();
dvander@1908
   296
dvander@1908
   297
	/* Move its "paused time" to its base (known) time, 
dvander@1908
   298
	 * then reset the start/end.  Note that since CalcAtomTime() 
dvander@1908
   299
	 * reads the base time, we SHOULD NOT use += to add.
dvander@1908
   300
	 */
dvander@1908
   301
	atom.base_time = CalcAtomTime(atom);
dvander@1908
   302
	atom.base_time += addTime;
dvander@1908
   303
	InitProfPoint(atom.start);
dvander@1908
   304
	atom.end.is_set = false;
dvander@1908
   305
}
dvander@1908
   306
dvander@1908
   307
void ProfileEngine::Clear()
dvander@1908
   308
{
dvander@1908
   309
	m_Natives.Clear();
dvander@1908
   310
	m_Callbacks.Clear();
dvander@1908
   311
	m_Functions.Clear();
dvander@1908
   312
	InitProfPoint(m_ProfStart);
dvander@1908
   313
}
dvander@1908
   314
dvander@1908
   315
void ProfileEngine::OnSourceModAllInitialized()
dvander@1908
   316
{
dvander@2728
   317
	rootmenu->AddRootConsoleCommand2("profiler", "Profiler commands", this);
dvander@1908
   318
}
dvander@1908
   319
dvander@1908
   320
void ProfileEngine::OnSourceModShutdown()
dvander@1908
   321
{
dvander@2728
   322
	rootmenu->RemoveRootConsoleCommand("profiler", this);
dvander@1908
   323
}
dvander@1908
   324
dvander@2728
   325
void ProfileEngine::OnRootConsoleCommand2(const char *cmdname, const ICommandArgs *command)
dvander@1908
   326
{
dvander@2728
   327
	if (command->ArgC() >= 3)
dvander@1908
   328
	{
dvander@2728
   329
		if (strcmp(command->Arg(2), "flush") == 0)
dvander@1908
   330
		{
dvander@1908
   331
			FILE *fp;
dvander@1908
   332
			char path[256];
dvander@1908
   333
dvander@2728
   334
			g_pSM->BuildPath(Path_SM, path, sizeof(path), "logs/profile_%d.xml", (int)time(NULL));
dvander@1908
   335
dvander@1908
   336
			if ((fp = fopen(path, "wt")) == NULL)
dvander@1908
   337
			{
dvander@2728
   338
				rootmenu->ConsolePrint("Failed, could not open file for writing: %s", path);
dvander@1908
   339
				return;
dvander@1908
   340
			}
dvander@1908
   341
dvander@1908
   342
			GenerateReport(fp);
dvander@1908
   343
dvander@1908
   344
			fclose(fp);
Kilandor@3161
   345
			
Kilandor@3161
   346
			Clear();
dvander@1908
   347
dvander@2728
   348
			rootmenu->ConsolePrint("Profiler report generated as: %s\n", path);
dvander@1908
   349
dvander@1908
   350
			return;
dvander@1908
   351
		}
Kilandor@3161
   352
		else if (strcmp(command->Arg(2), "report") == 0)
Kilandor@3161
   353
		{
Kilandor@3161
   354
			FILE *fp;
Kilandor@3161
   355
			char path[256];
Kilandor@3161
   356
Kilandor@3161
   357
			g_pSM->BuildPath(Path_SM, path, sizeof(path), "logs/profile_%d.xml", (int)time(NULL));
Kilandor@3161
   358
Kilandor@3161
   359
			if ((fp = fopen(path, "wt")) == NULL)
Kilandor@3161
   360
			{
Kilandor@3161
   361
				rootmenu->ConsolePrint("Failed, could not open file for writing: %s", path);
Kilandor@3161
   362
				return;
Kilandor@3161
   363
			}
Kilandor@3161
   364
Kilandor@3161
   365
			GenerateReport(fp);
Kilandor@3161
   366
Kilandor@3161
   367
			fclose(fp);
Kilandor@3161
   368
			
Kilandor@3161
   369
			rootmenu->ConsolePrint("Profiler report generated as: %s\n", path);
Kilandor@3161
   370
			return;
Kilandor@3161
   371
		}
Kilandor@3161
   372
		else if (strcmp(command->Arg(2), "clear") == 0)
Kilandor@3161
   373
		{
Kilandor@3161
   374
			Clear();
Kilandor@3161
   375
Kilandor@3161
   376
			rootmenu->ConsolePrint("Profiler statistics cleared.\n");
Kilandor@3161
   377
			return;
Kilandor@3161
   378
		}
dvander@1908
   379
	}
dvander@1908
   380
dvander@2728
   381
	rootmenu->ConsolePrint("Profiler commands:");
dvander@2728
   382
	rootmenu->DrawGenericOption("flush", "Flushes statistics to disk and starts over");
Kilandor@3161
   383
	rootmenu->DrawGenericOption("report", "Flushes statistics to disk");
Kilandor@3161
   384
	rootmenu->DrawGenericOption("clear", "Clears statistics");
dvander@1908
   385
}
dvander@1908
   386
dvander@1908
   387
bool ProfileEngine::GenerateReport(FILE *fp)
dvander@1908
   388
{
dvander@1908
   389
	time_t t;
dvander@1908
   390
	double total_time;
dvander@1908
   391
	prof_point_t end_time;
dvander@1908
   392
dvander@1908
   393
	InitProfPoint(end_time);
dvander@1908
   394
	total_time = DiffProfPoints(m_ProfStart, end_time);
dvander@1908
   395
dvander@1908
   396
	t = time(NULL);
dvander@1908
   397
dvander@1908
   398
	fprintf(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n");
dvander@1908
   399
	fprintf(fp, "<profile time=\"%d\" uptime=\"%f\">\n", (int)t, total_time);
dvander@1908
   400
	WriteReport(fp, &m_Natives, "natives");
dvander@1908
   401
	WriteReport(fp, &m_Callbacks, "callbacks");
dvander@1908
   402
	WriteReport(fp, &m_Functions, "functions");
dvander@1908
   403
	fprintf(fp, "</profile>\n");
dvander@1908
   404
dvander@1908
   405
	return true;
dvander@1908
   406
}
dvander@1908
   407
dvander@1908
   408
void ProfileEngine::WriteReport(FILE *fp, ProfileReport *report, const char *name)
dvander@1908
   409
{
dvander@1908
   410
	size_t i, num;
dvander@1908
   411
	prof_atom_report_t *ar;
dvander@1908
   412
	char new_name[512];
dvander@1908
   413
dvander@1908
   414
	fprintf(fp, " <report name=\"%s\">\n", name);
dvander@1908
   415
	
dvander@1908
   416
	num = report->GetNumReports();
dvander@1908
   417
	for (i = 0; i < num; i++)
dvander@1908
   418
	{
dvander@1908
   419
		ar = report->GetReport(i);
dvander@1908
   420
dvander@3005
   421
		strncopy(new_name, ar->atom_name, sizeof(new_name));
dvander@3005
   422
		UTIL_ReplaceAll(new_name, sizeof(new_name), "<", "&lt;", true);
dvander@3005
   423
		UTIL_ReplaceAll(new_name, sizeof(new_name), ">", "&gt;", true);
dvander@1908
   424
dvander@1908
   425
		fprintf(fp, "  <item name=\"%s\" numcalls=\"%d\" mintime=\"%f\" maxtime=\"%f\" totaltime=\"%f\"/>\n", 
dvander@1908
   426
			new_name,
dvander@1908
   427
			ar->num_calls,
dvander@1908
   428
			ar->min_time,
dvander@1908
   429
			ar->max_time,
dvander@1908
   430
			ar->total_time);
dvander@1908
   431
	}
dvander@1908
   432
dvander@1908
   433
	fprintf(fp, " </report>\n");
dvander@1908
   434
}
dvander@1908
   435
dvander@1908
   436
ProfileReport::~ProfileReport()
dvander@1908
   437
{
dvander@1908
   438
	for (size_t i = 0; i < m_Reports.size(); i++)
dvander@1908
   439
	{
dvander@1908
   440
		delete m_Reports[i];
dvander@1908
   441
	}
dvander@1908
   442
}
dvander@1908
   443
dvander@1908
   444
void ProfileReport::Clear()
dvander@1908
   445
{
dvander@1908
   446
	m_ReportLookup.clear();
dvander@1908
   447
	for (size_t i = 0; i < m_Reports.size(); i++)
dvander@1908
   448
	{
dvander@1908
   449
		delete m_Reports[i];
dvander@1908
   450
	}
dvander@1908
   451
	m_Reports.clear();
dvander@1908
   452
}
dvander@1908
   453
dvander@1908
   454
size_t ProfileReport::GetNumReports()
dvander@1908
   455
{
dvander@1908
   456
	return m_Reports.size();
dvander@1908
   457
}
dvander@1908
   458
dvander@1908
   459
prof_atom_report_t *ProfileReport::GetReport(size_t i)
dvander@1908
   460
{
dvander@1908
   461
	return m_Reports[i];
dvander@1908
   462
}
dvander@1908
   463
dvander@1908
   464
void ProfileReport::SaveAtom(const prof_atom_t &atom)
dvander@1908
   465
{
dvander@1908
   466
	double atom_time;
dvander@1908
   467
	char full_name[256];
dvander@4068
   468
	prof_atom_report_t *report;
dvander@1908
   469
dvander@1908
   470
	if (atom.atom_type == SP_PROF_NATIVES)
dvander@1908
   471
	{
dvander@2728
   472
		smcore.strncopy(full_name, atom.name, sizeof(full_name));
dvander@1908
   473
	}
dvander@1908
   474
	else
dvander@1908
   475
	{
dvander@2728
   476
		IPlugin *pl;
dvander@1908
   477
		const char *file;
dvander@1908
   478
dvander@1908
   479
		file = "unknown";
dvander@2728
   480
		if ((pl = pluginsys->FindPluginByContext(atom.ctx)) != NULL)
dvander@1908
   481
		{
dvander@1908
   482
			file = pl->GetFilename();
dvander@1908
   483
		}
dvander@1908
   484
dvander@2728
   485
		smcore.Format(full_name, sizeof(full_name), "%s!%s", file, atom.name);
dvander@1908
   486
	}
dvander@1908
   487
dvander@1908
   488
	atom_time = CalcAtomTime(atom);
dvander@1908
   489
dvander@4068
   490
	if (!m_ReportLookup.retrieve(full_name, &report))
dvander@1908
   491
	{
dvander@1908
   492
		report = new prof_atom_report_t;
dvander@1908
   493
dvander@2728
   494
		smcore.strncopy(report->atom_name, full_name, sizeof(report->atom_name));
dvander@1908
   495
		report->max_time = atom_time;
dvander@1908
   496
		report->min_time = atom_time;
dvander@1908
   497
		report->num_calls = 1;
dvander@1908
   498
		report->total_time = atom_time;
dvander@1908
   499
		
dvander@1908
   500
		m_ReportLookup.insert(full_name, report);
dvander@1908
   501
		m_Reports.push_back(report);
dvander@1908
   502
	}
dvander@1908
   503
	else
dvander@1908
   504
	{
dvander@1908
   505
		if (atom_time > report->max_time)
dvander@1908
   506
		{
dvander@1908
   507
			report->max_time = atom_time;
dvander@1908
   508
		}
dvander@1908
   509
		if (atom_time < report->min_time)
dvander@1908
   510
		{
dvander@1908
   511
			report->min_time = atom_time;
dvander@1908
   512
		}
dvander@1908
   513
		report->num_calls++;
dvander@1908
   514
		report->total_time += atom_time;
dvander@1908
   515
	}
dvander@1908
   516
}