20,741
edits
No edit summary |
m (→Proof of concept (patch): use collapsible template) |
||
Line 286: | Line 286: | ||
Here's a first stab at a simple subsystem to monitor FlightGear memory usage on Linux at 5 second intervals, consider it a "proof of concept" prototype now, as this would need to be cleaned up and implemented for Mac/Windows respectively - on Linux it simply works such that it merely fopen()s /proc/pid/smaps and copies two metrics to the property tree[http://codepad.org/27VcoMSX/raw.txt]: | Here's a first stab at a simple subsystem to monitor FlightGear memory usage on Linux at 5 second intervals, consider it a "proof of concept" prototype now, as this would need to be cleaned up and implemented for Mac/Windows respectively - on Linux it simply works such that it merely fopen()s /proc/pid/smaps and copies two metrics to the property tree[http://codepad.org/27VcoMSX/raw.txt]: | ||
< | |||
{{collapsible script|type=patch (diff)|title=sigar integration [http://codepad.org/27VcoMSX/raw.txt]|intro=|lang=diff|script= | |||
diff --git a/CMakeLists.txt b/CMakeLists.txt | |||
index 4808f9d..0771e15 100644 | |||
--- a/CMakeLists.txt | |||
+++ b/CMakeLists.txt | |||
@@ -194,6 +194,7 @@ option(ENABLE_TESTS "Set to ON to build test applications (default)" ON) | |||
option(ENABLE_FGCOM "Set to ON to build the FGCom application (default)" ON) | |||
option(ENABLE_FLITE "Set to ON to build the Flite text-to-speech module" ON) | |||
option(ENABLE_QT "Set to ON to build the internal Qt launcher" ON) | |||
+option(ENABLE_SIGAR "Set to ON to build with SIGAR support (experimental)" ON) | |||
if(LOGGING) | |||
# nothing | |||
@@ -328,6 +329,17 @@ if(ENABLE_PROFILE) | |||
message(STATUS "Built-in profiler using gperftools available") | |||
endif() | |||
+if(ENABLE_SIGAR) | |||
+ find_package(SIGAR REQUIRED) | |||
+ if(SIGAR_FOUND) | |||
+ set(FG_HAVE_SIGAR 1) | |||
+ message(STATUS "Built-in process monitoring using SIGAR available") | |||
+ else() | |||
+ message(STATUS "SIGAR libs not found (system-wide installation required for now)") | |||
+ endif() | |||
+endif(ENABLE_SIGAR) | |||
+ | |||
+ | |||
if(ENABLE_RTI) | |||
message(STATUS "RTI: ENABLED") | |||
find_package(RTI) | |||
diff --git a/CMakeModules/FindSIGAR.cmake b/CMakeModules/FindSIGAR.cmake | |||
new file mode 100644 | |||
index 0000000..4c890a7 | |||
--- /dev/null | |||
+++ b/CMakeModules/FindSIGAR.cmake | |||
@@ -0,0 +1,52 @@ | |||
+# - Find SIGAR: https://support.hyperic.com/display/SIGAR/Home | |||
+# Find the native SIGAR includes and library | |||
+# | |||
+# SIGAR_INCLUDE_DIR - where to find SIGAR.h, etc. | |||
+# SIGAR_LIBRARIES - List of libraries when using SIGAR. | |||
+# SIGAR_FOUND - True if SIGAR found. | |||
+# | |||
+# Redistribution and use is allowed according to the terms of the BSD license. | |||
+# For details see the accompanying COPYING-CMAKE-SCRIPTS file. | |||
+ | |||
+ | |||
+if (SIGAR_INCLUDE_DIR) | |||
+ # Already in cache, be silent | |||
+ set(SIGAR_FIND_QUIETLY TRUE) | |||
+endif () | |||
+ | |||
+find_path(SIGAR_INCLUDE_DIR sigar.h | |||
+ /opt/local/include | |||
+ /usr/local/include | |||
+ /usr/include | |||
+) | |||
+ | |||
+# SIGAR support a lot more platforms than listed here. | |||
+# cf. sigar.hyperic.com | |||
+set(SIGAR_NAMES sigar-x86-linux sigar-x86_64-linux sigar-amd64-linux sigar-universal-macosx sigar) | |||
+find_library(SIGAR_LIBRARY | |||
+ NAMES ${SIGAR_NAMES} | |||
+ PATHS /usr/lib /usr/local/lib /opt/local/lib | |||
+) | |||
+ | |||
+if (SIGAR_INCLUDE_DIR AND SIGAR_LIBRARY) | |||
+ set(SIGAR_FOUND TRUE) | |||
+ set(SIGAR_LIBRARIES ${SIGAR_LIBRARY} ${CMAKE_DL_LIBS}) | |||
+else () | |||
+ set(SIGAR_FOUND FALSE) | |||
+ set(SIGAR_LIBRARIES) | |||
+endif () | |||
+ | |||
+if (SIGAR_FOUND) | |||
+ message(STATUS "Found SIGAR: ${SIGAR_LIBRARIES}") | |||
+else () | |||
+ message(STATUS "Not Found SIGAR: ${SIGAR_LIBRARY}") | |||
+ if (SIGAR_FIND_REQUIRED) | |||
+ message(STATUS "Looked for SIGAR libraries named ${SIGAR_NAMES}.") | |||
+ message(FATAL_ERROR "Could NOT find SIGAR library") | |||
+ endif () | |||
+endif () | |||
+ | |||
+mark_as_advanced( | |||
+ SIGAR_LIBRARY | |||
+ SIGAR_INCLUDE_DIR | |||
+) | |||
diff --git a/src/Main/CMakeLists.txt b/src/Main/CMakeLists.txt | |||
index 4b6926e..06db7f7 100644 | |||
--- a/src/Main/CMakeLists.txt | |||
+++ b/src/Main/CMakeLists.txt | |||
@@ -17,6 +17,7 @@ set(SOURCES | |||
main.cxx | |||
options.cxx | |||
util.cxx | |||
+ sigar.cxx | |||
positioninit.cxx | |||
subsystemFactory.cxx | |||
screensaver_control.cxx | |||
@@ -35,12 +36,14 @@ set(HEADERS | |||
main.hxx | |||
options.hxx | |||
util.hxx | |||
+ sigar.hxx | |||
positioninit.hxx | |||
subsystemFactory.hxx | |||
AircraftDirVisitorBase.hxx | |||
screensaver_control.hxx | |||
) | |||
+ | |||
get_property(FG_SOURCES GLOBAL PROPERTY FG_SOURCES) | |||
get_property(FG_HEADERS GLOBAL PROPERTY FG_HEADERS) | |||
@@ -144,6 +147,7 @@ target_link_libraries(fgfs | |||
${SIMGEAR_CORE_LIBRARY_DEPENDENCIES} | |||
${SIMGEAR_SCENE_LIBRARY_DEPENDENCIES} | |||
${PLATFORM_LIBS} | |||
+ ${SIGAR_LIBRARIES} | |||
) | |||
if(ENABLE_FLITE) | |||
diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx | |||
index 30ffa0e..2b9bf7f 100644 | |||
--- a/src/Main/fg_init.cxx | |||
+++ b/src/Main/fg_init.cxx | |||
@@ -141,6 +141,7 @@ | |||
#include "globals.hxx" | |||
#include "logger.hxx" | |||
#include "main.hxx" | |||
+#include "sigar.hxx" | |||
#include "positioninit.hxx" | |||
#include "util.hxx" | |||
#include "AircraftDirVisitorBase.hxx" | |||
@@ -715,6 +716,10 @@ void fgCreateSubsystems(bool duringReset) { | |||
//////////////////////////////////////////////////////////////////// | |||
globals->add_subsystem("properties", new FGProperties); | |||
+ //////////////////////////////////////////////////////////////////// | |||
+ // Add the RAM/VRAM utilization statistics system (SIGAR) | |||
+ //////////////////////////////////////////////////////////////////// | |||
+ globals->add_subsystem("sigar", new ProcessInternals, SGSubsystemMgr::INIT, 1.00); | |||
//////////////////////////////////////////////////////////////////// | |||
// Add the performance monitoring system. | |||
diff --git a/src/Main/globals.cxx b/src/Main/globals.cxx | |||
index d41e00d..82f60d4 100644 | |||
--- a/src/Main/globals.cxx | |||
+++ b/src/Main/globals.cxx | |||
@@ -63,6 +63,7 @@ | |||
#include "fg_props.hxx" | |||
#include "fg_io.hxx" | |||
+#include "sigar.hxx" | |||
class AircraftResourceProvider : public simgear::ResourceProvider | |||
{ | |||
@@ -503,7 +504,12 @@ FGGlobals::add_subsystem (const char * name, | |||
SGSubsystemMgr::GroupType type, | |||
double min_time_sec) | |||
{ | |||
+ ProcessInternals* stats = (ProcessInternals*) subsystem_mgr->get_subsystem("sigar"); | |||
subsystem_mgr->add(name, subsystem, type, min_time_sec); | |||
+ if(stats) { | |||
+ SG_LOG(SG_GENERAL, SG_ALERT, "Process snapshot after adding subsystem:"<< name); | |||
+ //stats->snapshot(); | |||
+ } | |||
} | |||
SGSoundMgr * | |||
diff --git a/src/Main/sigar.cxx b/src/Main/sigar.cxx | |||
new file mode 100644 | |||
index 0000000..d10d5b8 | |||
--- /dev/null | |||
+++ b/src/Main/sigar.cxx | |||
@@ -0,0 +1,83 @@ | |||
+#include "sigar.hxx" | |||
+ | |||
+ProcessInternals::ProcessInternals() : | |||
+ total_ram("/sigar/memory/total-ram-mb"), | |||
+ arch("/sigar/architecture"), | |||
+ machine("/sigar/machine"), | |||
+ fgfspid("/sigar/process-id"), | |||
+ cores("/sigar/cpu/cores"), | |||
+ cpu_vendor("/sigar/cpu/vendor"), | |||
+ cpu_model("/sigar/cpu/model"), | |||
+ swap_used("/sigar/memory/swap-used-kb"), | |||
+ swap_free("/sigar/memory/swap-free-kb"), | |||
+ process_size("/sigar/process-size-kb"), | |||
+ process_threads("/sigar/process-threads") | |||
+{ | |||
+// initialize sigar | |||
+sigar_open(&t); | |||
+ | |||
+sigar_sys_info_t sysinfo; | |||
+sigar_sys_info_get(t, &sysinfo); | |||
+ | |||
+SG_LOG(SG_GENERAL, SG_DEBUG, "Architecture: " <<sysinfo.arch<<" machine:"<<sysinfo.machine); | |||
+arch=sysinfo.arch; | |||
+machine=sysinfo.machine; | |||
+ | |||
+// determine process ID | |||
+pid = sigar_pid_get(t); | |||
+assert(pid > 0); | |||
+SG_LOG(SG_GENERAL, SG_DEBUG, "fgfs pid is:" << (int)pid); | |||
+fgfspid = pid; | |||
+ | |||
+// get a list of CPUs | |||
+sigar_cpu_list_t cpulist; | |||
+sigar_cpu_list_get(t, &cpulist); | |||
+ | |||
+// Get info for each core | |||
+sigar_cpu_info_list_t cpuinfo; | |||
+sigar_cpu_info_list_get(t, &cpuinfo); | |||
+ | |||
+// print out vendor/model of the CPU | |||
+sigar_cpu_info_t info = cpuinfo.data[0]; | |||
+SG_LOG(SG_GENERAL, SG_DEBUG, "CPUs: "<<cpulist.number <<"("<<info.vendor << " " <<info.model<<")"); | |||
+cores=cpulist.number; | |||
+cpu_vendor=info.vendor; | |||
+cpu_model=info.model; | |||
+ | |||
+sigar_cpu_list_destroy(t, &cpulist); | |||
+ | |||
+// get memory info for whole system | |||
+sigar_mem_t system; | |||
+status = sigar_mem_get(t,&system); | |||
+ | |||
+sigar_proc_state_get(t, pid, &proc_stat); | |||
+ | |||
+ //_ram = new SIGAR_Wrapper(); //FIXME: should be implemented for Win/Mac & Linux (use SIGAR) | |||
+SG_LOG(SG_GENERAL, SG_DEBUG, "SIGAR reported total ram:" << system.ram << "mb"); | |||
+total_ram=system.ram; | |||
+} | |||
+ | |||
+ProcessInternals::~ProcessInternals() { | |||
+ //delete _ram; | |||
+sigar_close(t); | |||
+} | |||
+ | |||
+void | |||
+ProcessInternals::update(double dt) { | |||
+update(); | |||
+sigar_proc_mem_get(t, pid, &mem); | |||
+SG_LOG(SG_GENERAL, SG_DEBUG, "SIGAR reported process size:" << mem.size/1024 <<" kb"); | |||
+process_size=mem.size/1024; | |||
+ | |||
+SG_LOG(SG_GENERAL, SG_DEBUG, "Active fgfs threads:" << proc_stat.threads ); | |||
+process_threads=proc_stat.threads; | |||
+ | |||
+sigar_swap_get(t, &swap); | |||
+SG_LOG(SG_GENERAL, SG_DEBUG, "swap used:" << swap.used/1024 <<"kb / free:" << swap.free/1024 <<"kb"); | |||
+swap_used=swap.used/1024; | |||
+swap_free=swap.free/1024; | |||
+ | |||
+} | |||
+ | |||
+ | |||
+ | |||
diff --git a/src/Main/sigar.hxx b/src/Main/sigar.hxx | |||
new file mode 100644 | |||
index 0000000..999eaee | |||
--- /dev/null | |||
+++ b/src/Main/sigar.hxx | |||
@@ -0,0 +1,120 @@ | |||
+#ifndef __RAM_USAGE | |||
+#define __RAM_USAGE | |||
+ | |||
+#include<stdio.h> | |||
+#include<stdlib.h> | |||
+#include<X11/X.h> | |||
+#include<X11/Xlib.h> | |||
+#include<GL/gl.h> | |||
+#include<GL/glx.h> | |||
+#include<GL/glu.h> | |||
+ | |||
+#include <sigar.h> | |||
+ | |||
+// http://wiki.flightgear.org/Howto:Use_Property_Tree_Objects | |||
+#include <simgear/props/propertyObject.hxx> | |||
+ | |||
+#include <simgear/timing/timestamp.hxx> | |||
+ | |||
+// http://wiki.flightgear.org/Howto:Create_new_subsystems | |||
+#include <simgear/structure/subsystem_mgr.hxx> | |||
+ | |||
+#include <Main/globals.hxx> | |||
+#include <Main/fg_props.hxx> | |||
+ | |||
+#include <string> | |||
+#include <map> | |||
+ | |||
+#include "vram_tracking.hxx" | |||
+ | |||
+using std::map; | |||
+ | |||
+class SIGAR_Wrapper { | |||
+public: | |||
+ virtual ~SIGAR_Wrapper() { | |||
+ delete _gpu; | |||
+ }; | |||
+ SIGAR_Wrapper() { | |||
+ // see if we have a GPU that supports vram tracking: | |||
+ | |||
+ std::string fallback = "NVIDIA"; //default value | |||
+ | |||
+ // get the actual GL vendor string from the property tree, using the fallback | |||
+ std::string glvendor = fgGetString("/sim/rendering/gl-vendor",fallback.c_str() ); | |||
+ | |||
+ // make it upper case: http://stackoverflow.com/questions/735204/convert-a-string-in-c-to-upper-case | |||
+ std::transform(glvendor.begin(), glvendor.end(), glvendor.begin(), ::toupper); | |||
+ | |||
+ // look for the "NVIDIA" substring: http://www.cplusplus.com/reference/string/string/find/ | |||
+ std::size_t found = glvendor.find("NVIDIA"); | |||
+ if (found!=std::string::npos) { | |||
+ _gpu = new NVIDIA_GPU(glvendor); | |||
+ } | |||
+ else if (glvendor.find("INTEL") != std::string::npos) { | |||
+ _gpu = new INTEL_GPU(glvendor); | |||
+ } | |||
+ else if (glvendor.find("ATI") != std::string::npos) { | |||
+ _gpu = new ATI_GPU(glvendor); | |||
+ } | |||
+ else { | |||
+ _gpu = new UNKNOWN_GPU(glvendor); | |||
+ } | |||
+} | |||
+ typedef map<const char*, double> RamMap; | |||
+ virtual void update() { | |||
+ | |||
+} | |||
+ | |||
+ void updateVRAMStats() const { | |||
+if (_gpu) | |||
+_gpu->updateVRAMStats(); | |||
+} | |||
+ | |||
+protected: | |||
+ RamMap _size; | |||
+ GPUInfo* _gpu; // wraps VRAM utilization gathering | |||
+}; | |||
+ | |||
+class ProcessInternals : public SGSubsystem | |||
+{ | |||
+public: | |||
+ ProcessInternals(); | |||
+ ~ProcessInternals(); | |||
+ virtual void update(double); | |||
+ void snapshot() { | |||
+ update(); | |||
+ simgear::PropertyObject<int> used_vram_kb ("/stats/vram/used-kb"); | |||
+ //SG_LOG(SG_GENERAL, SG_ALERT, "Process size: " <<_ram->_total_size << "kb" << std::endl); | |||
+ // SG_LOG(SG_GENERAL, SG_ALERT, "VRAM utilization: " << used_vram_kb << " kb" << std::endl); | |||
+ } | |||
+ | |||
+protected: | |||
+private: | |||
+ simgear::PropertyObject<int> total_ram; | |||
+ simgear::PropertyObject<std::string> arch; | |||
+ simgear::PropertyObject<std::string> machine; | |||
+ simgear::PropertyObject<int> fgfspid; | |||
+ simgear::PropertyObject<int> cores; | |||
+ simgear::PropertyObject<std::string> cpu_vendor; | |||
+ simgear::PropertyObject<std::string> cpu_model; | |||
+ simgear::PropertyObject<int> swap_used; // swap used by process | |||
+ simgear::PropertyObject<int> swap_free; | |||
+ simgear::PropertyObject<int> process_size; | |||
+ simgear::PropertyObject<int> process_threads; | |||
+// SIGAR stuff | |||
+ int status; | |||
+ sigar_pid_t pid; // stores the process identifier | |||
+ sigar_t *t; // handle to sigar | |||
+ sigar_proc_state_t proc_stat; // process specific state (i.e. number of active threads) | |||
+ sigar_proc_mem_t mem; // for getting memory info from sigar (total/used RAM etc) | |||
+ sigar_swap_t swap; // global swap info | |||
+ | |||
+ SIGAR_Wrapper* _ram; | |||
+ void update() { | |||
+ //_ram->update(); // will update ram/swap utilization using SIGAR | |||
+ //_ram->updateVRAMStats(); // will update vram utilization by using NVIDIA/AMD APIs | |||
+ } | |||
+}; | |||
+ | |||
+#endif | |||
+ | |||
diff --git a/src/Main/vram_tracking.hxx b/src/Main/vram_tracking.hxx | |||
new file mode 100644 | |||
index 0000000..375c339 | |||
--- /dev/null | |||
+++ b/src/Main/vram_tracking.hxx | |||
@@ -0,0 +1,85 @@ | |||
+#ifndef __VRAM_TRACKING_HXX__ | |||
+#define __VRAM_TRACKING_HXX__ | |||
+ // this is the base class, with the only method being a virtual method | |||
+ // which needs to be implemented by any child classes | |||
+ // a pointer of this class will be added to the LinuxMemoryInterface class | |||
+ class GPUInfo { | |||
+ private: | |||
+ protected: | |||
+ GLint total_mem_kb; | |||
+ GLint cur_avail_mem_kb; | |||
+ simgear::PropertyObject<int> total_vram_kb,available_vram_kb, used_vram_kb; | |||
+ public: | |||
+ GPUInfo(std::string &glvendor): total_mem_kb(-1), | |||
+ cur_avail_mem_kb(-1), | |||
+ total_vram_kb("/stats/vram/total-size-kb"), | |||
+ available_vram_kb ("/stats/vram/available-kb"), | |||
+ used_vram_kb ("/stats/vram/used-kb") { | |||
+ SG_LOG(SG_GENERAL, SG_ALERT, "VRAM Tracking: Supported GPU found:"<< glvendor); | |||
+ } | |||
+ virtual ~GPUInfo() {} | |||
+ virtual void updateVRAMStats() {}; | |||
+ }; //GPUInfo | |||
+ | |||
+ // Actually implement the GPUInfo class for all 3 GPU vendors: | |||
+ | |||
+// http://www.geeks3d.com/20100531/programming-tips-how-to-know-the-graphics-memory-size-and-usage-in-opengl/ | |||
+#define GL_GPU_MEM_INFO_TOTAL_AVAILABLE_MEM_NVX 0x9048 | |||
+#define GL_GPU_MEM_INFO_CURRENT_AVAILABLE_MEM_NVX 0x9049 | |||
+ | |||
+ struct NVIDIA_GPU: public GPUInfo { | |||
+ NVIDIA_GPU(std::string glvendor) : GPUInfo(glvendor) { | |||
+ // determine total memory and store it (this wont change at runtime, so only do it once) | |||
+ glGetIntegerv(GL_GPU_MEM_INFO_TOTAL_AVAILABLE_MEM_NVX, &total_mem_kb); | |||
+ SG_LOG(SG_GENERAL, SG_DEBUG, "NVIDIA GPU with total memory: " << total_mem_kb << " kbytes"); | |||
+ total_vram_kb = total_mem_kb; | |||
+ } | |||
+ virtual void updateVRAMStats() { | |||
+ | |||
+ glGetIntegerv(GL_GPU_MEM_INFO_CURRENT_AVAILABLE_MEM_NVX, &cur_avail_mem_kb); | |||
+ SG_LOG(SG_GENERAL, SG_DEBUG,"NVIDIA VRAM tracking function says:"<<total_mem_kb<<" (total) available:"<<cur_avail_mem_kb); | |||
+ // update property objects | |||
+ available_vram_kb = cur_avail_mem_kb; | |||
+ used_vram_kb = total_vram_kb - available_vram_kb; | |||
+ } | |||
+ }; //NVIDIA_GPU | |||
+ | |||
+ struct ATI_GPU: public GPUInfo { | |||
+ ATI_GPU(std::string glvendor) : GPUInfo(glvendor) { | |||
+ } | |||
+ // https://github.com/OpenGLInsights/OpenGLInsightsCode/blob/master/Chapter%2038%20Monitoring%20Graphics%20Memory%20Usage/Ch38AMD/Ch38AMD/Ch38AMDDlg.cpp#L280 | |||
+ virtual void updateVRAMStats() { | |||
+// https://github.com/OpenGLInsights/OpenGLInsightsCode/blob/master/Chapter%2038%20Monitoring%20Graphics%20Memory%20Usage/Ch38AMD/Ch38AMD/Ch38AMDDlg.cpp | |||
+#ifndef GL_VBO_FREE_MEMORY_ATI | |||
+ #define GL_VBO_FREE_MEMORY_ATI 0x87FB | |||
+ #define GL_TEXTURE_FREE_MEMORY_ATI 0x87FC | |||
+ #define GL_RENDERBUFFER_FREE_MEMORY_ATI 0x87FD | |||
+#endif | |||
+ | |||
+ SG_LOG(SG_GENERAL, SG_ALERT,"Sorry:ATI VRAM tracking function not yet implemented (nvidia only)!"); | |||
+ used_vram_kb = total_vram_kb = available_vram_kb = -1; | |||
+ } | |||
+ };//ATI_GPU | |||
+ | |||
+ struct INTEL_GPU : public GPUInfo { | |||
+ INTEL_GPU(std::string glvendor) : GPUInfo(glvendor) { | |||
+} | |||
+ virtual void updateVRAMStat() { | |||
+ SG_LOG(SG_GENERAL, SG_ALERT,"Sorry:Intel VRAM tracking function not yet implemented (nvidia only)!"); | |||
+ used_vram_kb = total_vram_kb = available_vram_kb = -1; | |||
+ } | |||
+ | |||
+ }; // INTEL_GPU | |||
+ | |||
+ struct UNKNOWN_GPU: public GPUInfo { | |||
+ UNKNOWN_GPU(std::string glvendor) : GPUInfo(glvendor) { | |||
+ } | |||
+ virtual void updateVRAMStat() { | |||
+ SG_LOG(SG_GENERAL, SG_DEBUG,"Sorry:VRAM tracking function not yet implemented for your GPU (nvidia only)!"); | |||
+ used_vram_kb = total_vram_kb = available_vram_kb = -1; | |||
+ } | |||
+ | |||
+ }; // UNKNOWN_GPU | |||
+ | |||
+ | |||
+#endif | |||
|conc=|bgcolor=}} | |||
== History == | == History == |