diff --git a/source/network/NetServer.cpp b/source/network/NetServer.cpp index 8ab3d4243d..892a4db036 100644 --- a/source/network/NetServer.cpp +++ b/source/network/NetServer.cpp @@ -47,7 +47,7 @@ static CStr DebugName(CNetServerSession* session) } CNetServer::CNetServer() : - m_ScriptInterface(new ScriptInterface("Engine")), m_NextHostID(1), m_Host(NULL), m_Stats(NULL) + m_ScriptInterface(new ScriptInterface("Engine", "Net server")), m_NextHostID(1), m_Host(NULL), m_Stats(NULL) { m_State = SERVER_STATE_UNCONNECTED; diff --git a/source/network/NetStats.cpp b/source/network/NetStats.cpp index 6634f550f3..880c9de8f1 100644 --- a/source/network/NetStats.cpp +++ b/source/network/NetStats.cpp @@ -54,7 +54,10 @@ CStr CNetStatsTable::GetName() CStr CNetStatsTable::GetTitle() { - return "Network statistics"; + if (m_Host) + return "Network host statistics"; + else + return "Network client statistics"; } size_t CNetStatsTable::GetNumberRows() diff --git a/source/ps/GameSetup/GameSetup.cpp b/source/ps/GameSetup/GameSetup.cpp index 2ab27c1541..5ae856e5e4 100644 --- a/source/ps/GameSetup/GameSetup.cpp +++ b/source/ps/GameSetup/GameSetup.cpp @@ -69,6 +69,7 @@ #include "scripting/ScriptGlue.h" #include "scriptinterface/ScriptInterface.h" +#include "scriptinterface/ScriptStats.h" #include "maths/scripting/JSInterface_Vector3D.h" @@ -628,6 +629,8 @@ void Shutdown(int UNUSED(flags)) CNetHost::Deinitialize(); + SAFE_DELETE(g_ScriptStatsTable); + // should be last, since the above use them SAFE_DELETE(g_Logger); delete &g_Profiler; @@ -729,6 +732,9 @@ void Init(const CmdLineArgs& args, int flags) new CProfileViewer; new CProfileManager; // before any script code + g_ScriptStatsTable = new CScriptStatsTable; + g_ProfileViewer.AddRootTable(g_ScriptStatsTable); + MICROLOG(L"init scripting"); InitScripting(); // before GUI diff --git a/source/ps/Profile.cpp b/source/ps/Profile.cpp index 0cb8ba1d5d..1f955acf05 100644 --- a/source/ps/Profile.cpp +++ b/source/ps/Profile.cpp @@ -657,5 +657,5 @@ void CProfileManager::StructuralReset() delete( root ); root = new CProfileNode( "root", NULL ); current = root; - g_ProfileViewer.AddRootTable(root->display_table); + g_ProfileViewer.AddRootTable(root->display_table, true); } diff --git a/source/ps/ProfileViewer.cpp b/source/ps/ProfileViewer.cpp index e9e3b65e13..0fb3ae662a 100644 --- a/source/ps/ProfileViewer.cpp +++ b/source/ps/ProfileViewer.cpp @@ -37,7 +37,6 @@ #include "lib/res/graphics/unifont.h" #include "renderer/Renderer.h" -#define LOG_CATEGORY L"profiler" extern int g_xres, g_yres; struct CProfileViewerInternals @@ -340,9 +339,12 @@ InReaction CProfileViewer::InputThunk(const SDL_Event_* ev) // Add a table to the list of roots -void CProfileViewer::AddRootTable(AbstractProfileTable* table) +void CProfileViewer::AddRootTable(AbstractProfileTable* table, bool front) { - m->rootTables.push_back(table); + if (front) + m->rootTables.insert(m->rootTables.begin(), table); + else + m->rootTables.push_back(table); } namespace @@ -441,7 +443,7 @@ void CProfileViewer::SaveToFile() if (m->outputStream.fail()) { - LOG(CLogger::Error, LOG_CATEGORY, L"Failed to open profile log file"); + LOGERROR(L"Failed to open profile log file"); return; } } diff --git a/source/ps/ProfileViewer.h b/source/ps/ProfileViewer.h index 03fe3d6718..ce3144dfef 100644 --- a/source/ps/ProfileViewer.h +++ b/source/ps/ProfileViewer.h @@ -161,8 +161,10 @@ public: * the list of root tables when they are deleted. * * @param table This table is added as a root table. + * @param front If true then the table will be the new first in the list, + * else it will be the last. */ - void AddRootTable(AbstractProfileTable* table); + void AddRootTable(AbstractProfileTable* table, bool front = false); /** * InputThunk: Delegate to the singleton's Input() member function diff --git a/source/scripting/ScriptGlue.cpp b/source/scripting/ScriptGlue.cpp index a1d127ad48..191e7173eb 100644 --- a/source/scripting/ScriptGlue.cpp +++ b/source/scripting/ScriptGlue.cpp @@ -35,9 +35,10 @@ #include "graphics/scripting/JSInterface_LightEnv.h" #include "gui/GUIManager.h" #include "gui/IGUIObject.h" -#include "lib/timer.h" -#include "lib/svn_revision.h" #include "lib/frequency_filter.h" +#include "lib/svn_revision.h" +#include "lib/timer.h" +#include "lib/utf8.h" #include "maths/scripting/JSInterface_Vector3D.h" #include "network/NetServer.h" #include "ps/CConsole.h" @@ -467,41 +468,34 @@ JSBool GetBuildTimestamp( JSContext* cx, JSObject*, uintN argc, jsval* argv, jsv return JS_TRUE; } - -// Return distance between 2 points. -// params: 2 position vectors [CVector3D] -// returns: Euclidean distance [float] -JSBool ComputeDistanceBetweenTwoPoints( JSContext* cx, JSObject* UNUSED(obj), uintN argc, jsval* argv, jsval* rval ) +#ifdef DEBUG +void DumpHeap(const char* name, int idx, JSContext* cx) { - JSU_REQUIRE_PARAMS(2); - - CVector3D* a = ToNative( argv[0] ); - CVector3D* b = ToNative( argv[1] ); - float dist = ( *a - *b ).Length(); - *rval = ToJSVal( dist ); - return( JS_TRUE ); + wchar_t buf[64]; + swprintf_s(buf, ARRAY_SIZE(buf), L"%hs.%03d.txt", name, idx); + fs::wpath path(psLogDir()/buf); + FILE* f = fopen(utf8_from_wstring(path.string()).c_str(), "w"); + debug_assert(f); + JS_DumpHeap(cx, f, NULL, 0, NULL, (size_t)-1, NULL); + fclose(f); } +#endif - -// Returns the global object. -// params: -// returns: global object -// notes: -// - Useful for accessing an object from another scope. -JSBool GetGlobal( JSContext* cx, JSObject* globalObject, uintN argc, jsval* argv, jsval* rval ) +JSBool DumpHeaps(JSContext* UNUSED(cx), JSObject* UNUSED(globalObject), uintN UNUSED(argc), jsval* UNUSED(argv), jsval* UNUSED(rval) ) { - JSU_REQUIRE_NO_PARAMS(); +#ifdef DEBUG + static int i = 0; - *rval = OBJECT_TO_JSVAL( globalObject ); - return( JS_TRUE ); -} + if (ScriptingHost::IsInitialised()) + DumpHeap("gui", i, g_ScriptingHost.GetContext()); + if (g_Game) + DumpHeap("sim", i, g_Game->GetSimulation2()->GetScriptInterface().GetContext()); -// Saves the current profiling data to the logs/profile.txt file -JSBool SaveProfileData( JSContext* cx, JSObject* UNUSED(globalObject), uintN argc, jsval* argv, jsval* rval ) -{ - JSU_REQUIRE_NO_PARAMS(); - g_ProfileViewer.SaveToFile(); - return( JS_TRUE ); + ++i; +#else + debug_warn(L"DumpHeaps only available in DEBUG mode"); +#endif + return JS_TRUE; } // Toggles drawing the sky @@ -664,10 +658,8 @@ JSFunctionSpec ScriptFunctionTable[] = JS_FUNC("getGUIObjectByName", GetGUIObjectByName, 1) // Miscellany - JS_FUNC("v3dist", ComputeDistanceBetweenTwoPoints, 2) JS_FUNC("buildTime", GetBuildTimestamp, 0) - JS_FUNC("getGlobal", GetGlobal, 0) - JS_FUNC("saveProfileData", SaveProfileData, 0) + JS_FUNC("dumpHeaps", DumpHeaps, 0) // end of table marker {0, 0, 0, 0, 0} diff --git a/source/scripting/ScriptingHost.cpp b/source/scripting/ScriptingHost.cpp index 193875a262..0fc3e85de0 100644 --- a/source/scripting/ScriptingHost.cpp +++ b/source/scripting/ScriptingHost.cpp @@ -31,7 +31,7 @@ ScriptingHost::ScriptingHost() { - m_ScriptInterface = new ScriptInterface("Engine"); + m_ScriptInterface = new ScriptInterface("Engine", "GUI"); m_Context = m_ScriptInterface->GetContext(); diff --git a/source/scriptinterface/ScriptInterface.cpp b/source/scriptinterface/ScriptInterface.cpp index 2efd9b4be0..91785b7f39 100644 --- a/source/scriptinterface/ScriptInterface.cpp +++ b/source/scriptinterface/ScriptInterface.cpp @@ -18,6 +18,7 @@ #include "precompiled.h" #include "ScriptInterface.h" +#include "ScriptStats.h" #include "AutoRooters.h" #include "lib/debug.h" @@ -34,7 +35,7 @@ #include "valgrind.h" -const int RUNTIME_SIZE = 4 * 1024 * 1024; // TODO: how much memory is needed? +const int RUNTIME_SIZE = 8 * 1024 * 1024; // TODO: how much memory is needed? const int STACK_CHUNK_SIZE = 8192; #ifdef NDEBUG @@ -241,6 +242,7 @@ ScriptInterface_impl::ScriptInterface_impl(const char* nativeScopeName, JSContex JS_SetOptions(m_cx, JSOPTION_STRICT // "warn on dubious practice" | JSOPTION_XML // "ECMAScript for XML support: parse as a token" | JSOPTION_VAROBJFIX // "recommended" (fixes variable scoping) +// | JSOPTION_JIT ); JS_SetVersion(m_cx, JSVERSION_LATEST); @@ -281,13 +283,17 @@ void ScriptInterface_impl::Register(const char* name, JSNative fptr, uintN nargs JS_DefineFunction(m_cx, m_nativeScope, name, fptr, nargs, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT); } -ScriptInterface::ScriptInterface(const char* nativeScopeName, JSContext* cx) : - m(new ScriptInterface_impl(nativeScopeName, cx)) +ScriptInterface::ScriptInterface(const char* nativeScopeName, const char* debugName) : + m(new ScriptInterface_impl(nativeScopeName, NULL)) { + if (g_ScriptStatsTable) + g_ScriptStatsTable->Add(this, debugName); } ScriptInterface::~ScriptInterface() { + if (g_ScriptStatsTable) + g_ScriptStatsTable->Remove(this); } void ScriptInterface::ShutDown() @@ -335,6 +341,11 @@ JSContext* ScriptInterface::GetContext() const return m->m_cx; } +JSRuntime* ScriptInterface::GetRuntime() const +{ + return m->m_rt; +} + bool ScriptInterface::AddRoot(void* ptr, const char* name) { return JS_AddNamedRoot(m->m_cx, ptr, name) ? true : false; diff --git a/source/scriptinterface/ScriptInterface.h b/source/scriptinterface/ScriptInterface.h index 009bdb0f90..c84c6e6a39 100644 --- a/source/scriptinterface/ScriptInterface.h +++ b/source/scriptinterface/ScriptInterface.h @@ -50,7 +50,7 @@ public: * @param cx NULL if the object should create and manage its own context; otherwise * an existing context which it will share */ - ScriptInterface(const char* nativeScopeName, JSContext* cx = NULL); + ScriptInterface(const char* nativeScopeName, const char* debugName = "Unknown"); ~ScriptInterface(); @@ -64,6 +64,7 @@ public: static void* GetCallbackData(JSContext* cx); JSContext* GetContext() const; + JSRuntime* GetRuntime() const; void ReplaceNondeterministicFunctions(boost::rand48& rng); diff --git a/source/scriptinterface/ScriptStats.cpp b/source/scriptinterface/ScriptStats.cpp new file mode 100644 index 0000000000..32582fa225 --- /dev/null +++ b/source/scriptinterface/ScriptStats.cpp @@ -0,0 +1,121 @@ +/* Copyright (C) 2010 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 0 A.D. If not, see . + */ + +#include "precompiled.h" + +#include "ScriptStats.h" + +#include "scriptinterface/ScriptInterface.h" + +#include "js/jsapi.h" + +CScriptStatsTable* g_ScriptStatsTable; + +enum +{ + Row_MaxBytes, + Row_MaxMallocBytes, + Row_Bytes, + Row_NumberGC, + NumberRows +}; + +CScriptStatsTable::CScriptStatsTable() +{ +} + +void CScriptStatsTable::Add(const ScriptInterface* scriptInterface, const std::string& title) +{ + m_ScriptInterfaces.push_back(std::make_pair(scriptInterface, title)); +} + +void CScriptStatsTable::Remove(const ScriptInterface* scriptInterface) +{ + for (size_t i = 0; i < m_ScriptInterfaces.size(); ) + { + if (m_ScriptInterfaces[i].first == scriptInterface) + m_ScriptInterfaces.erase(m_ScriptInterfaces.begin() + i); + else + ++i; + } +} + +CStr CScriptStatsTable::GetName() +{ + return "script"; +} + +CStr CScriptStatsTable::GetTitle() +{ + return "Script statistics"; +} + +size_t CScriptStatsTable::GetNumberRows() +{ + return NumberRows; +} + +const std::vector& CScriptStatsTable::GetColumns() +{ + m_ColumnDescriptions.clear(); + m_ColumnDescriptions.push_back(ProfileColumn("Name", 200)); + for (size_t i = 0; i < m_ScriptInterfaces.size(); ++i) + m_ColumnDescriptions.push_back(ProfileColumn(m_ScriptInterfaces[i].second, 80)); + return m_ColumnDescriptions; +} + +CStr CScriptStatsTable::GetCellText(size_t row, size_t col) +{ + switch(row) + { + case Row_MaxBytes: + { + if (col == 0) + return "max nominal heap bytes"; + uint32_t n = JS_GetGCParameter(m_ScriptInterfaces.at(col-1).first->GetRuntime(), JSGC_MAX_BYTES); + return CStr(n); + } + case Row_MaxMallocBytes: + { + if (col == 0) + return "max JS_malloc bytes"; + uint32_t n = JS_GetGCParameter(m_ScriptInterfaces.at(col-1).first->GetRuntime(), JSGC_MAX_MALLOC_BYTES); + return CStr(n); + } + case Row_Bytes: + { + if (col == 0) + return "allocated bytes"; + uint32_t n = JS_GetGCParameter(m_ScriptInterfaces.at(col-1).first->GetRuntime(), JSGC_BYTES); + return CStr(n); + } + case Row_NumberGC: + { + if (col == 0) + return "number of GCs"; + uint32_t n = JS_GetGCParameter(m_ScriptInterfaces.at(col-1).first->GetRuntime(), JSGC_NUMBER); + return CStr(n); + } + default: + return "???"; + } +} + +AbstractProfileTable* CScriptStatsTable::GetChild(size_t UNUSED(row)) +{ + return 0; +} diff --git a/source/scriptinterface/ScriptStats.h b/source/scriptinterface/ScriptStats.h new file mode 100644 index 0000000000..95c79e9763 --- /dev/null +++ b/source/scriptinterface/ScriptStats.h @@ -0,0 +1,50 @@ +/* Copyright (C) 2010 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 0 A.D. If not, see . + */ + +#ifndef INCLUDED_SCRIPTSTATS +#define INCLUDED_SCRIPTSTATS + +#include "ps/ProfileViewer.h" + +class ScriptInterface; + +class CScriptStatsTable : public AbstractProfileTable +{ + NONCOPYABLE(CScriptStatsTable); +public: + CScriptStatsTable(); + + void Add(const ScriptInterface* scriptInterface, const std::string& title); + void Remove(const ScriptInterface* scriptInterface); + + virtual CStr GetName(); + virtual CStr GetTitle(); + virtual size_t GetNumberRows(); + virtual const std::vector& GetColumns(); + virtual CStr GetCellText(size_t row, size_t col); + virtual AbstractProfileTable* GetChild(size_t row); + +private: + std::vector > m_ScriptInterfaces; + std::vector m_ColumnDescriptions; +}; + +// To simplify the UI we want to use a single table for all script interfaces, +// so just make it a global that they can all add themselves to +extern CScriptStatsTable* g_ScriptStatsTable; + +#endif // INCLUDED_SCRIPTSTATS diff --git a/source/simulation2/system/ComponentManager.cpp b/source/simulation2/system/ComponentManager.cpp index d69542f996..8d5f62aa7d 100644 --- a/source/simulation2/system/ComponentManager.cpp +++ b/source/simulation2/system/ComponentManager.cpp @@ -52,7 +52,8 @@ public: }; CComponentManager::CComponentManager(CSimContext& context, bool skipScriptFunctions) : - m_NextScriptComponentTypeId(CID__LastNative), m_ScriptInterface("Engine"), m_SimContext(context), m_CurrentlyHotloading(false) + m_NextScriptComponentTypeId(CID__LastNative), m_ScriptInterface("Engine", "Simulation"), + m_SimContext(context), m_CurrentlyHotloading(false) { context.SetComponentManager(this);