Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
Alchemy Viewer
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Iterations
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Locked files
Deploy
Releases
Package registry
Operate
Terraform modules
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Terms and privacy
Keyboard shortcuts
?
Snippets
Groups
Projects
Silent mode is enabled
All outbound communications are blocked.
Learn more
.
Show more breadcrumbs
Alchemy Viewer
Alchemy Viewer
Commits
fc5aaf5d
Commit
fc5aaf5d
authored
13 years ago
by
Oz Linden
Browse files
Options
Downloads
Plain Diff
merge changes for storm-1509
parents
4d432251
ed648b1f
No related branches found
Branches containing commit
No related tags found
Tags containing commit
No related merge requests found
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
indra/llcommon/llsys.cpp
+675
-35
675 additions, 35 deletions
indra/llcommon/llsys.cpp
indra/llcommon/llsys.h
+22
-0
22 additions, 0 deletions
indra/llcommon/llsys.h
with
697 additions
and
35 deletions
indra/llcommon/llsys.cpp
+
675
−
35
View file @
fc5aaf5d
/**
/**
* @file llsys.cpp
* @file llsys.cpp
* @brief Imp
e
lementation of the basic system query functions.
* @brief Implementation of the basic system query functions.
*
*
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
* Second Life Viewer Source Code
...
@@ -24,6 +24,10 @@
...
@@ -24,6 +24,10 @@
* $/LicenseInfo$
* $/LicenseInfo$
*/
*/
#if LL_WINDOWS
#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
#endif
#include
"linden_common.h"
#include
"linden_common.h"
#include
"llsys.h"
#include
"llsys.h"
...
@@ -36,22 +40,43 @@
...
@@ -36,22 +40,43 @@
#endif
#endif
#include
"llprocessor.h"
#include
"llprocessor.h"
#include
"llerrorcontrol.h"
#include
"llevents.h"
#include
"lltimer.h"
#include
"llsdserialize.h"
#include
"llsdutil.h"
#include
<boost/bind.hpp>
#include
<boost/circular_buffer.hpp>
#include
<boost/regex.hpp>
#include
<boost/foreach.hpp>
#include
<boost/lexical_cast.hpp>
#include
<boost/range.hpp>
#include
<boost/utility/enable_if.hpp>
#include
<boost/type_traits/is_integral.hpp>
#include
<boost/type_traits/is_float.hpp>
using
namespace
llsd
;
#if LL_WINDOWS
#if LL_WINDOWS
# define WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# include <winsock2.h>
# include <winsock2.h>
# include <windows.h>
# include <windows.h>
# include <psapi.h> // GetPerformanceInfo() et al.
#elif LL_DARWIN
#elif LL_DARWIN
# include <errno.h>
# include <errno.h>
# include <sys/sysctl.h>
# include <sys/sysctl.h>
# include <sys/utsname.h>
# include <sys/utsname.h>
# include <stdint.h>
# include <stdint.h>
# include <Carbon/Carbon.h>
# include <Carbon/Carbon.h>
# include <sys/wait.h>
# include <string.h>
# include <stdexcept>
#elif LL_LINUX
#elif LL_LINUX
# include <errno.h>
# include <errno.h>
# include <sys/utsname.h>
# include <sys/utsname.h>
# include <unistd.h>
# include <unistd.h>
# include <sys/sysinfo.h>
# include <sys/sysinfo.h>
# include <stdexcept>
const
char
MEMINFO_FILE
[]
=
"/proc/meminfo"
;
const
char
MEMINFO_FILE
[]
=
"/proc/meminfo"
;
#elif LL_SOLARIS
#elif LL_SOLARIS
# include <stdio.h>
# include <stdio.h>
...
@@ -70,6 +95,15 @@ extern int errno;
...
@@ -70,6 +95,15 @@ extern int errno;
static
const
S32
CPUINFO_BUFFER_SIZE
=
16383
;
static
const
S32
CPUINFO_BUFFER_SIZE
=
16383
;
LLCPUInfo
gSysCPU
;
LLCPUInfo
gSysCPU
;
// Don't log memory info any more often than this. It also serves as our
// framerate sample size.
static
const
F32
MEM_INFO_THROTTLE
=
20
;
// Sliding window of samples. We intentionally limit the length of time we
// remember "the slowest" framerate because framerate is very slow at login.
// If we only triggered FrameWatcher logging when the session framerate
// dropped below the login framerate, we'd have very little additional data.
static
const
F32
MEM_INFO_WINDOW
=
10
*
60
;
#if LL_WINDOWS
#if LL_WINDOWS
#ifndef DLLVERSIONINFO
#ifndef DLLVERSIONINFO
typedef
struct
_DllVersionInfo
typedef
struct
_DllVersionInfo
...
@@ -613,8 +647,78 @@ void LLCPUInfo::stream(std::ostream& s) const
...
@@ -613,8 +647,78 @@ void LLCPUInfo::stream(std::ostream& s) const
s
<<
"->mCPUString: "
<<
mCPUString
<<
std
::
endl
;
s
<<
"->mCPUString: "
<<
mCPUString
<<
std
::
endl
;
}
}
// Helper class for LLMemoryInfo: accumulate stats in the form we store for
// LLMemoryInfo::getStatsMap().
class
Stats
{
public:
Stats
()
:
mStats
(
LLSD
::
emptyMap
())
{}
// Store every integer type as LLSD::Integer.
template
<
class
T
>
void
add
(
const
LLSD
::
String
&
name
,
const
T
&
value
,
typename
boost
::
enable_if
<
boost
::
is_integral
<
T
>
>::
type
*
=
0
)
{
mStats
[
name
]
=
LLSD
::
Integer
(
value
);
}
// Store every floating-point type as LLSD::Real.
template
<
class
T
>
void
add
(
const
LLSD
::
String
&
name
,
const
T
&
value
,
typename
boost
::
enable_if
<
boost
::
is_float
<
T
>
>::
type
*
=
0
)
{
mStats
[
name
]
=
LLSD
::
Real
(
value
);
}
// Hope that LLSD::Date values are sufficiently unambiguous.
void
add
(
const
LLSD
::
String
&
name
,
const
LLSD
::
Date
&
value
)
{
mStats
[
name
]
=
value
;
}
LLSD
get
()
const
{
return
mStats
;
}
private
:
LLSD
mStats
;
};
// Wrap boost::regex_match() with a function that doesn't throw.
template
<
typename
S
,
typename
M
,
typename
R
>
static
bool
regex_match_no_exc
(
const
S
&
string
,
M
&
match
,
const
R
&
regex
)
{
try
{
return
boost
::
regex_match
(
string
,
match
,
regex
);
}
catch
(
const
std
::
runtime_error
&
e
)
{
LL_WARNS
(
"LLMemoryInfo"
)
<<
"error matching with '"
<<
regex
.
str
()
<<
"': "
<<
e
.
what
()
<<
":
\n
'"
<<
string
<<
"'"
<<
LL_ENDL
;
return
false
;
}
}
// Wrap boost::regex_search() with a function that doesn't throw.
template
<
typename
S
,
typename
M
,
typename
R
>
static
bool
regex_search_no_exc
(
const
S
&
string
,
M
&
match
,
const
R
&
regex
)
{
try
{
return
boost
::
regex_search
(
string
,
match
,
regex
);
}
catch
(
const
std
::
runtime_error
&
e
)
{
LL_WARNS
(
"LLMemoryInfo"
)
<<
"error searching with '"
<<
regex
.
str
()
<<
"': "
<<
e
.
what
()
<<
":
\n
'"
<<
string
<<
"'"
<<
LL_ENDL
;
return
false
;
}
}
LLMemoryInfo
::
LLMemoryInfo
()
LLMemoryInfo
::
LLMemoryInfo
()
{
{
refresh
();
}
}
#if LL_WINDOWS
#if LL_WINDOWS
...
@@ -638,11 +742,7 @@ static U32 LLMemoryAdjustKBResult(U32 inKB)
...
@@ -638,11 +742,7 @@ static U32 LLMemoryAdjustKBResult(U32 inKB)
U32
LLMemoryInfo
::
getPhysicalMemoryKB
()
const
U32
LLMemoryInfo
::
getPhysicalMemoryKB
()
const
{
{
#if LL_WINDOWS
#if LL_WINDOWS
MEMORYSTATUSEX
state
;
return
LLMemoryAdjustKBResult
(
mStatsMap
[
"Total Physical KB"
].
asInteger
());
state
.
dwLength
=
sizeof
(
state
);
GlobalMemoryStatusEx
(
&
state
);
return
LLMemoryAdjustKBResult
((
U32
)(
state
.
ullTotalPhys
>>
10
));
#elif LL_DARWIN
#elif LL_DARWIN
// This might work on Linux as well. Someone check...
// This might work on Linux as well. Someone check...
...
@@ -690,12 +790,82 @@ U32 LLMemoryInfo::getPhysicalMemoryClamped() const
...
@@ -690,12 +790,82 @@ U32 LLMemoryInfo::getPhysicalMemoryClamped() const
void
LLMemoryInfo
::
getAvailableMemoryKB
(
U32
&
avail_physical_mem_kb
,
U32
&
avail_virtual_mem_kb
)
void
LLMemoryInfo
::
getAvailableMemoryKB
(
U32
&
avail_physical_mem_kb
,
U32
&
avail_virtual_mem_kb
)
{
{
#if LL_WINDOWS
#if LL_WINDOWS
MEMORYSTATUSEX
state
;
// Sigh, this shouldn't be a static method, then we wouldn't have to
state
.
dwLength
=
sizeof
(
state
);
// reload this data separately from refresh()
GlobalMemoryStatusEx
(
&
state
);
LLSD
statsMap
(
loadStatsMap
());
avail_physical_mem_kb
=
statsMap
[
"Avail Physical KB"
].
asInteger
();
avail_virtual_mem_kb
=
statsMap
[
"Avail Virtual KB"
].
asInteger
();
#elif LL_DARWIN
// mStatsMap is derived from vm_stat, look for (e.g.) "kb free":
// $ vm_stat
// Mach Virtual Memory Statistics: (page size of 4096 bytes)
// Pages free: 462078.
// Pages active: 142010.
// Pages inactive: 220007.
// Pages wired down: 159552.
// "Translation faults": 220825184.
// Pages copy-on-write: 2104153.
// Pages zero filled: 167034876.
// Pages reactivated: 65153.
// Pageins: 2097212.
// Pageouts: 41759.
// Object cache: 841598 hits of 7629869 lookups (11% hit rate)
avail_physical_mem_kb
=
-
1
;
avail_virtual_mem_kb
=
-
1
;
avail_physical_mem_kb
=
(
U32
)(
state
.
ullAvailPhys
/
1024
)
;
#elif LL_LINUX
avail_virtual_mem_kb
=
(
U32
)(
state
.
ullAvailVirtual
/
1024
)
;
// mStatsMap is derived from MEMINFO_FILE:
// $ cat /proc/meminfo
// MemTotal: 4108424 kB
// MemFree: 1244064 kB
// Buffers: 85164 kB
// Cached: 1990264 kB
// SwapCached: 0 kB
// Active: 1176648 kB
// Inactive: 1427532 kB
// Active(anon): 529152 kB
// Inactive(anon): 15924 kB
// Active(file): 647496 kB
// Inactive(file): 1411608 kB
// Unevictable: 16 kB
// Mlocked: 16 kB
// HighTotal: 3266316 kB
// HighFree: 721308 kB
// LowTotal: 842108 kB
// LowFree: 522756 kB
// SwapTotal: 6384632 kB
// SwapFree: 6384632 kB
// Dirty: 28 kB
// Writeback: 0 kB
// AnonPages: 528820 kB
// Mapped: 89472 kB
// Shmem: 16324 kB
// Slab: 159624 kB
// SReclaimable: 145168 kB
// SUnreclaim: 14456 kB
// KernelStack: 2560 kB
// PageTables: 5560 kB
// NFS_Unstable: 0 kB
// Bounce: 0 kB
// WritebackTmp: 0 kB
// CommitLimit: 8438844 kB
// Committed_AS: 1271596 kB
// VmallocTotal: 122880 kB
// VmallocUsed: 65252 kB
// VmallocChunk: 52356 kB
// HardwareCorrupted: 0 kB
// HugePages_Total: 0
// HugePages_Free: 0
// HugePages_Rsvd: 0
// HugePages_Surp: 0
// Hugepagesize: 2048 kB
// DirectMap4k: 434168 kB
// DirectMap2M: 477184 kB
// (could also run 'free', but easier to read a file than run a program)
avail_physical_mem_kb
=
-
1
;
avail_virtual_mem_kb
=
-
1
;
#else
#else
//do not know how to collect available memory info for other systems.
//do not know how to collect available memory info for other systems.
...
@@ -708,56 +878,389 @@ void LLMemoryInfo::getAvailableMemoryKB(U32& avail_physical_mem_kb, U32& avail_v
...
@@ -708,56 +878,389 @@ void LLMemoryInfo::getAvailableMemoryKB(U32& avail_physical_mem_kb, U32& avail_v
void
LLMemoryInfo
::
stream
(
std
::
ostream
&
s
)
const
void
LLMemoryInfo
::
stream
(
std
::
ostream
&
s
)
const
{
{
// We want these memory stats to be easy to grep from the log, along with
// the timestamp. So preface each line with the timestamp and a
// distinctive marker. Without that, we'd have to search the log for the
// introducer line, then read subsequent lines, etc...
std
::
string
pfx
(
LLError
::
utcTime
()
+
" <mem> "
);
// Max key length
size_t
key_width
(
0
);
BOOST_FOREACH
(
const
MapEntry
&
pair
,
inMap
(
mStatsMap
))
{
size_t
len
(
pair
.
first
.
length
());
if
(
len
>
key_width
)
{
key_width
=
len
;
}
}
// Now stream stats
BOOST_FOREACH
(
const
MapEntry
&
pair
,
inMap
(
mStatsMap
))
{
s
<<
pfx
<<
std
::
setw
(
key_width
+
1
)
<<
(
pair
.
first
+
':'
)
<<
' '
;
LLSD
value
(
pair
.
second
);
if
(
value
.
isInteger
())
s
<<
std
::
setw
(
12
)
<<
value
.
asInteger
();
else
if
(
value
.
isReal
())
s
<<
std
::
fixed
<<
std
::
setprecision
(
1
)
<<
value
.
asReal
();
else
if
(
value
.
isDate
())
value
.
asDate
().
toStream
(
s
);
else
s
<<
value
;
// just use default LLSD formatting
s
<<
std
::
endl
;
}
}
LLSD
LLMemoryInfo
::
getStatsMap
()
const
{
return
mStatsMap
;
}
LLMemoryInfo
&
LLMemoryInfo
::
refresh
()
{
mStatsMap
=
loadStatsMap
();
LL_DEBUGS
(
"LLMemoryInfo"
)
<<
"Populated mStatsMap:
\n
"
;
LLSDSerialize
::
toPrettyXML
(
mStatsMap
,
LL_CONT
);
LL_ENDL
;
return
*
this
;
}
LLSD
LLMemoryInfo
::
loadStatsMap
()
{
// This implementation is derived from stream() code (as of 2011-06-29).
Stats
stats
;
// associate timestamp for analysis over time
stats
.
add
(
"timestamp"
,
LLDate
::
now
());
#if LL_WINDOWS
#if LL_WINDOWS
MEMORYSTATUSEX
state
;
MEMORYSTATUSEX
state
;
state
.
dwLength
=
sizeof
(
state
);
state
.
dwLength
=
sizeof
(
state
);
GlobalMemoryStatusEx
(
&
state
);
GlobalMemoryStatusEx
(
&
state
);
s
<<
"Percent Memory use: "
<<
(
U32
)
state
.
dwMemoryLoad
<<
'%'
<<
std
::
endl
;
stats
.
add
(
"Percent Memory use"
,
state
.
dwMemoryLoad
);
s
<<
"Total Physical KB: "
<<
(
U32
)(
state
.
ullTotalPhys
/
1024
)
<<
std
::
endl
;
stats
.
add
(
"Total Physical KB"
,
state
.
ullTotalPhys
/
1024
);
s
<<
"Avail Physical KB: "
<<
(
U32
)(
state
.
ullAvailPhys
/
1024
)
<<
std
::
endl
;
stats
.
add
(
"Avail Physical KB"
,
state
.
ullAvailPhys
/
1024
);
s
<<
"Total page KB: "
<<
(
U32
)(
state
.
ullTotalPageFile
/
1024
)
<<
std
::
endl
;
stats
.
add
(
"Total page KB"
,
state
.
ullTotalPageFile
/
1024
);
s
<<
"Avail page KB: "
<<
(
U32
)(
state
.
ullAvailPageFile
/
1024
)
<<
std
::
endl
;
stats
.
add
(
"Avail page KB"
,
state
.
ullAvailPageFile
/
1024
);
s
<<
"Total Virtual KB: "
<<
(
U32
)(
state
.
ullTotalVirtual
/
1024
)
<<
std
::
endl
;
stats
.
add
(
"Total Virtual KB"
,
state
.
ullTotalVirtual
/
1024
);
s
<<
"Avail Virtual KB: "
<<
(
U32
)(
state
.
ullAvailVirtual
/
1024
)
<<
std
::
endl
;
stats
.
add
(
"Avail Virtual KB"
,
state
.
ullAvailVirtual
/
1024
);
PERFORMANCE_INFORMATION
perf
;
perf
.
cb
=
sizeof
(
perf
);
GetPerformanceInfo
(
&
perf
,
sizeof
(
perf
));
SIZE_T
pagekb
(
perf
.
PageSize
/
1024
);
stats
.
add
(
"CommitTotal KB"
,
perf
.
CommitTotal
*
pagekb
);
stats
.
add
(
"CommitLimit KB"
,
perf
.
CommitLimit
*
pagekb
);
stats
.
add
(
"CommitPeak KB"
,
perf
.
CommitPeak
*
pagekb
);
stats
.
add
(
"PhysicalTotal KB"
,
perf
.
PhysicalTotal
*
pagekb
);
stats
.
add
(
"PhysicalAvail KB"
,
perf
.
PhysicalAvailable
*
pagekb
);
stats
.
add
(
"SystemCache KB"
,
perf
.
SystemCache
*
pagekb
);
stats
.
add
(
"KernelTotal KB"
,
perf
.
KernelTotal
*
pagekb
);
stats
.
add
(
"KernelPaged KB"
,
perf
.
KernelPaged
*
pagekb
);
stats
.
add
(
"KernelNonpaged KB"
,
perf
.
KernelNonpaged
*
pagekb
);
stats
.
add
(
"PageSize KB"
,
pagekb
);
stats
.
add
(
"HandleCount"
,
perf
.
HandleCount
);
stats
.
add
(
"ProcessCount"
,
perf
.
ProcessCount
);
stats
.
add
(
"ThreadCount"
,
perf
.
ThreadCount
);
PROCESS_MEMORY_COUNTERS_EX
pmem
;
pmem
.
cb
=
sizeof
(
pmem
);
// GetProcessMemoryInfo() is documented to accept either
// PROCESS_MEMORY_COUNTERS* or PROCESS_MEMORY_COUNTERS_EX*, presumably
// using the redundant size info to distinguish. But its prototype
// specifically accepts PROCESS_MEMORY_COUNTERS*, and since this is a
// classic-C API, PROCESS_MEMORY_COUNTERS_EX isn't a subclass. Cast the
// pointer.
GetProcessMemoryInfo
(
GetCurrentProcess
(),
PPROCESS_MEMORY_COUNTERS
(
&
pmem
),
sizeof
(
pmem
));
stats
.
add
(
"Page Fault Count"
,
pmem
.
PageFaultCount
);
stats
.
add
(
"PeakWorkingSetSize KB"
,
pmem
.
PeakWorkingSetSize
/
1024
);
stats
.
add
(
"WorkingSetSize KB"
,
pmem
.
WorkingSetSize
/
1024
);
stats
.
add
(
"QutaPeakPagedPoolUsage KB"
,
pmem
.
QuotaPeakPagedPoolUsage
/
1024
);
stats
.
add
(
"QuotaPagedPoolUsage KB"
,
pmem
.
QuotaPagedPoolUsage
/
1024
);
stats
.
add
(
"QuotaPeakNonPagedPoolUsage KB"
,
pmem
.
QuotaPeakNonPagedPoolUsage
/
1024
);
stats
.
add
(
"QuotaNonPagedPoolUsage KB"
,
pmem
.
QuotaNonPagedPoolUsage
/
1024
);
stats
.
add
(
"PagefileUsage KB"
,
pmem
.
PagefileUsage
/
1024
);
stats
.
add
(
"PeakPagefileUsage KB"
,
pmem
.
PeakPagefileUsage
/
1024
);
stats
.
add
(
"PrivateUsage KB"
,
pmem
.
PrivateUsage
/
1024
);
#elif LL_DARWIN
#elif LL_DARWIN
uint64_t
phys
=
0
;
uint64_t
phys
=
0
;
size_t
len
=
sizeof
(
phys
);
size_t
len
=
sizeof
(
phys
);
if
(
sysctlbyname
(
"hw.memsize"
,
&
phys
,
&
len
,
NULL
,
0
)
==
0
)
if
(
sysctlbyname
(
"hw.memsize"
,
&
phys
,
&
len
,
NULL
,
0
)
==
0
)
{
{
s
<<
"Total Physical KB
: "
<<
phys
/
1024
<<
std
::
endl
;
s
tats
.
add
(
"Total Physical KB
"
,
phys
/
1024
)
;
}
}
else
else
{
{
s
<<
"Unable to collect memory information"
;
LL_WARNS
(
"LLMemoryInfo"
)
<<
"Unable to collect hw.memsize memory information"
<<
LL_ENDL
;
}
FILE
*
pout
=
popen
(
"vm_stat 2>&1"
,
"r"
);
if
(
!
pout
)
// popen() couldn't run vm_stat
{
// Save errno right away.
int
popen_errno
(
errno
);
LL_WARNS
(
"LLMemoryInfo"
)
<<
"Unable to collect vm_stat memory information: "
;
char
buffer
[
256
];
if
(
0
==
strerror_r
(
popen_errno
,
buffer
,
sizeof
(
buffer
)))
{
LL_CONT
<<
buffer
;
}
else
{
LL_CONT
<<
"errno "
<<
popen_errno
;
}
LL_CONT
<<
LL_ENDL
;
}
else
// popen() launched vm_stat
{
// Mach Virtual Memory Statistics: (page size of 4096 bytes)
// Pages free: 462078.
// Pages active: 142010.
// Pages inactive: 220007.
// Pages wired down: 159552.
// "Translation faults": 220825184.
// Pages copy-on-write: 2104153.
// Pages zero filled: 167034876.
// Pages reactivated: 65153.
// Pageins: 2097212.
// Pageouts: 41759.
// Object cache: 841598 hits of 7629869 lookups (11% hit rate)
// Intentionally don't pass the boost::no_except flag. These
// boost::regex objects are constructed with string literals, so they
// should be valid every time. If they become invalid, we WANT an
// exception, hopefully even before the dev checks in.
boost
::
regex
pagesize_rx
(
"
\\
(page size of ([0-9]+) bytes
\\
)"
);
boost
::
regex
stat_rx
(
"(.+): +([0-9]+)
\\
."
);
boost
::
regex
cache_rx
(
"Object cache: ([0-9]+) hits of ([0-9]+) lookups "
"
\\
(([0-9]+)% hit rate
\\
)"
);
boost
::
cmatch
matched
;
LLSD
::
Integer
pagesizekb
(
4096
/
1024
);
// Here 'pout' is vm_stat's stdout. Search it for relevant data.
char
line
[
100
];
line
[
sizeof
(
line
)
-
1
]
=
'\0'
;
while
(
fgets
(
line
,
sizeof
(
line
)
-
1
,
pout
))
{
size_t
linelen
(
strlen
(
line
));
// Truncate any trailing newline
if
(
line
[
linelen
-
1
]
==
'\n'
)
{
line
[
--
linelen
]
=
'\0'
;
}
LL_DEBUGS
(
"LLMemoryInfo"
)
<<
line
<<
LL_ENDL
;
if
(
regex_search_no_exc
(
line
,
matched
,
pagesize_rx
))
{
// "Mach Virtual Memory Statistics: (page size of 4096 bytes)"
std
::
string
pagesize_str
(
matched
[
1
].
first
,
matched
[
1
].
second
);
try
{
// Reasonable to assume that pagesize will always be a
// multiple of 1Kb?
pagesizekb
=
boost
::
lexical_cast
<
LLSD
::
Integer
>
(
pagesize_str
)
/
1024
;
}
catch
(
const
boost
::
bad_lexical_cast
&
)
{
LL_WARNS
(
"LLMemoryInfo"
)
<<
"couldn't parse '"
<<
pagesize_str
<<
"' in vm_stat line: "
<<
line
<<
LL_ENDL
;
continue
;
}
stats
.
add
(
"page size"
,
pagesizekb
);
}
else
if
(
regex_match_no_exc
(
line
,
matched
,
stat_rx
))
{
// e.g. "Pages free: 462078."
// Strip double-quotes off certain statistic names
const
char
*
key_begin
(
matched
[
1
].
first
),
*
key_end
(
matched
[
1
].
second
);
if
(
key_begin
[
0
]
==
'"'
&&
key_end
[
-
1
]
==
'"'
)
{
++
key_begin
;
--
key_end
;
}
LLSD
::
String
key
(
key_begin
,
key_end
);
LLSD
::
String
value_str
(
matched
[
2
].
first
,
matched
[
2
].
second
);
LLSD
::
Integer
value
(
0
);
try
{
value
=
boost
::
lexical_cast
<
LLSD
::
Integer
>
(
value_str
);
}
catch
(
const
boost
::
bad_lexical_cast
&
)
{
LL_WARNS
(
"LLMemoryInfo"
)
<<
"couldn't parse '"
<<
value_str
<<
"' in vm_stat line: "
<<
line
<<
LL_ENDL
;
continue
;
}
// Store this statistic.
stats
.
add
(
key
,
value
);
// Is this in units of pages? If so, convert to Kb.
static
const
LLSD
::
String
pages
(
"Pages "
);
if
(
key
.
substr
(
0
,
pages
.
length
())
==
pages
)
{
// Synthesize a new key with kb in place of Pages
LLSD
::
String
kbkey
(
"kb "
);
kbkey
.
append
(
key
.
substr
(
pages
.
length
()));
stats
.
add
(
kbkey
,
value
*
pagesizekb
);
}
}
else
if
(
regex_match_no_exc
(
line
,
matched
,
cache_rx
))
{
// e.g. "Object cache: 841598 hits of 7629869 lookups (11% hit rate)"
static
const
char
*
cache_keys
[]
=
{
"cache hits"
,
"cache lookups"
,
"cache hit%"
};
std
::
vector
<
LLSD
::
Integer
>
cache_values
;
for
(
size_t
i
=
0
;
i
<
(
sizeof
(
cache_keys
)
/
sizeof
(
cache_keys
[
0
]));
++
i
)
{
LLSD
::
String
value_str
(
matched
[
i
+
1
].
first
,
matched
[
i
+
1
].
second
);
LLSD
::
Integer
value
(
0
);
try
{
value
=
boost
::
lexical_cast
<
LLSD
::
Integer
>
(
value_str
);
}
catch
(
boost
::
bad_lexical_cast
&
)
{
LL_WARNS
(
"LLMemoryInfo"
)
<<
"couldn't parse '"
<<
value_str
<<
"' in vm_stat line: "
<<
line
<<
LL_ENDL
;
continue
;
}
stats
.
add
(
cache_keys
[
i
],
value
);
}
}
else
{
LL_WARNS
(
"LLMemoryInfo"
)
<<
"unrecognized vm_stat line: "
<<
line
<<
LL_ENDL
;
}
}
int
status
(
pclose
(
pout
));
if
(
status
==
-
1
)
// pclose() couldn't retrieve rc
{
// Save errno right away.
int
pclose_errno
(
errno
);
// The ECHILD error happens so frequently that unless filtered,
// the warning below spams the log file. This is too bad, because
// sometimes the logic above fails to produce any output derived
// from vm_stat, but we've been unable to observe any specific
// error indicating the problem.
if
(
pclose_errno
!=
ECHILD
)
{
LL_WARNS
(
"LLMemoryInfo"
)
<<
"Unable to obtain vm_stat termination code: "
;
char
buffer
[
256
];
if
(
0
==
strerror_r
(
pclose_errno
,
buffer
,
sizeof
(
buffer
)))
{
LL_CONT
<<
buffer
;
}
else
{
LL_CONT
<<
"errno "
<<
pclose_errno
;
}
LL_CONT
<<
LL_ENDL
;
}
}
else
// pclose() retrieved rc; analyze
{
if
(
WIFEXITED
(
status
))
{
int
rc
(
WEXITSTATUS
(
status
));
if
(
rc
!=
0
)
{
LL_WARNS
(
"LLMemoryInfo"
)
<<
"vm_stat terminated with rc "
<<
rc
<<
LL_ENDL
;
}
}
else
if
(
WIFSIGNALED
(
status
))
{
LL_WARNS
(
"LLMemoryInfo"
)
<<
"vm_stat terminated by signal "
<<
WTERMSIG
(
status
)
<<
LL_ENDL
;
}
}
}
}
#elif LL_SOLARIS
#elif LL_SOLARIS
U64
phys
=
0
;
U64
phys
=
0
;
phys
=
(
U64
)(
sysconf
(
_SC_PHYS_PAGES
))
*
(
U64
)(
sysconf
(
_SC_PAGESIZE
)
/
1024
);
phys
=
(
U64
)(
sysconf
(
_SC_PHYS_PAGES
))
*
(
U64
)(
sysconf
(
_SC_PAGESIZE
)
/
1024
);
s
<<
"Total Physical KB
: "
<<
phys
<<
std
::
endl
;
stats
.
add
(
"Total Physical KB
"
,
phys
)
;
#else
// *NOTE: This works on linux. What will it do on other systems?
#elif LL_LINUX
LLFILE
*
meminfo
=
LLFile
::
fopen
(
MEMINFO_FILE
,
"rb"
);
std
::
ifstream
meminfo
(
MEMINFO_FILE
);
if
(
meminfo
)
if
(
meminfo
.
is_open
()
)
{
{
char
line
[
MAX_STRING
];
/* Flawfinder: ignore */
// MemTotal: 4108424 kB
memset
(
line
,
0
,
MAX_STRING
);
// MemFree: 1244064 kB
while
(
fgets
(
line
,
MAX_STRING
,
meminfo
))
// Buffers: 85164 kB
// Cached: 1990264 kB
// SwapCached: 0 kB
// Active: 1176648 kB
// Inactive: 1427532 kB
// ...
// VmallocTotal: 122880 kB
// VmallocUsed: 65252 kB
// VmallocChunk: 52356 kB
// HardwareCorrupted: 0 kB
// HugePages_Total: 0
// HugePages_Free: 0
// HugePages_Rsvd: 0
// HugePages_Surp: 0
// Hugepagesize: 2048 kB
// DirectMap4k: 434168 kB
// DirectMap2M: 477184 kB
// Intentionally don't pass the boost::no_except flag. This
// boost::regex object is constructed with a string literal, so it
// should be valid every time. If it becomes invalid, we WANT an
// exception, hopefully even before the dev checks in.
boost
::
regex
stat_rx
(
"(.+): +([0-9]+)( kB)?"
);
boost
::
smatch
matched
;
std
::
string
line
;
while
(
std
::
getline
(
meminfo
,
line
))
{
{
line
[
strlen
(
line
)
-
1
]
=
' '
;
/*Flawfinder: ignore*/
LL_DEBUGS
(
"LLMemoryInfo"
)
<<
line
<<
LL_ENDL
;
s
<<
line
;
if
(
regex_match_no_exc
(
line
,
matched
,
stat_rx
))
{
// e.g. "MemTotal: 4108424 kB"
LLSD
::
String
key
(
matched
[
1
].
first
,
matched
[
1
].
second
);
LLSD
::
String
value_str
(
matched
[
2
].
first
,
matched
[
2
].
second
);
LLSD
::
Integer
value
(
0
);
try
{
value
=
boost
::
lexical_cast
<
LLSD
::
Integer
>
(
value_str
);
}
catch
(
const
boost
::
bad_lexical_cast
&
)
{
LL_WARNS
(
"LLMemoryInfo"
)
<<
"couldn't parse '"
<<
value_str
<<
"' in "
<<
MEMINFO_FILE
<<
" line: "
<<
line
<<
LL_ENDL
;
continue
;
}
// Store this statistic.
stats
.
add
(
key
,
value
);
}
else
{
LL_WARNS
(
"LLMemoryInfo"
)
<<
"unrecognized "
<<
MEMINFO_FILE
<<
" line: "
<<
line
<<
LL_ENDL
;
}
}
}
fclose
(
meminfo
);
}
}
else
else
{
{
s
<<
"Unable to collect memory information"
;
LL_WARNS
(
"LLMemoryInfo"
)
<<
"Unable to collect memory information"
<<
LL_ENDL
;
}
}
#else
LL_WARNS
(
"LLMemoryInfo"
)
<<
"Unknown system; unable to collect memory information"
<<
LL_ENDL
;
#endif
#endif
return
stats
.
get
();
}
}
std
::
ostream
&
operator
<<
(
std
::
ostream
&
s
,
const
LLOSInfo
&
info
)
std
::
ostream
&
operator
<<
(
std
::
ostream
&
s
,
const
LLOSInfo
&
info
)
...
@@ -778,6 +1281,143 @@ std::ostream& operator<<(std::ostream& s, const LLMemoryInfo& info)
...
@@ -778,6 +1281,143 @@ std::ostream& operator<<(std::ostream& s, const LLMemoryInfo& info)
return
s
;
return
s
;
}
}
class
FrameWatcher
{
public:
FrameWatcher
()
:
// Hooking onto the "mainloop" event pump gets us one call per frame.
mConnection
(
LLEventPumps
::
instance
()
.
obtain
(
"mainloop"
)
.
listen
(
"FrameWatcher"
,
boost
::
bind
(
&
FrameWatcher
::
tick
,
this
,
_1
))),
// Initializing mSampleStart to an invalid timestamp alerts us to skip
// trying to compute framerate on the first call.
mSampleStart
(
-
1
),
// Initializing mSampleEnd to 0 ensures that we treat the first call
// as the completion of a sample window.
mSampleEnd
(
0
),
mFrames
(
0
),
// Both MEM_INFO_WINDOW and MEM_INFO_THROTTLE are in seconds. We need
// the number of integer MEM_INFO_THROTTLE sample slots that will fit
// in MEM_INFO_WINDOW. Round up.
mSamples
(
int
((
MEM_INFO_WINDOW
/
MEM_INFO_THROTTLE
)
+
0.7
)),
// Initializing to F32_MAX means that the first real frame will become
// the slowest ever, which sounds like a good idea.
mSlowest
(
F32_MAX
)
{}
bool
tick
(
const
LLSD
&
)
{
F32
timestamp
(
mTimer
.
getElapsedTimeF32
());
// Count this frame in the interval just completed.
++
mFrames
;
// Have we finished a sample window yet?
if
(
timestamp
<
mSampleEnd
)
{
// no, just keep waiting
return
false
;
}
// Set up for next sample window. Capture values for previous frame in
// local variables and reset data members.
U32
frames
(
mFrames
);
F32
sampleStart
(
mSampleStart
);
// No frames yet in next window
mFrames
=
0
;
// which starts right now
mSampleStart
=
timestamp
;
// and ends MEM_INFO_THROTTLE seconds in the future
mSampleEnd
=
mSampleStart
+
MEM_INFO_THROTTLE
;
// On the very first call, that's all we can do, no framerate
// computation is possible.
if
(
sampleStart
<
0
)
{
return
false
;
}
// How long did this actually take? As framerate slows, the duration
// of the frame we just finished could push us WELL beyond our desired
// sample window size.
F32
elapsed
(
timestamp
-
sampleStart
);
F32
framerate
(
frames
/
elapsed
);
// Remember previous slowest framerate because we're just about to
// update it.
F32
slowest
(
mSlowest
);
// Remember previous number of samples.
boost
::
circular_buffer
<
F32
>::
size_type
prevSize
(
mSamples
.
size
());
// Capture new framerate in our samples buffer. Once the buffer is
// full (after MEM_INFO_WINDOW seconds), this will displace the oldest
// sample. ("So they all rolled over, and one fell out...")
mSamples
.
push_back
(
framerate
);
// Calculate the new minimum framerate. I know of no way to update a
// rolling minimum without ever rescanning the buffer. But since there
// are only a few tens of items in this buffer, rescanning it is
// probably cheaper (and certainly easier to reason about) than
// attempting to optimize away some of the scans.
mSlowest
=
framerate
;
// pick an arbitrary entry to start
for
(
boost
::
circular_buffer
<
F32
>::
const_iterator
si
(
mSamples
.
begin
()),
send
(
mSamples
.
end
());
si
!=
send
;
++
si
)
{
if
(
*
si
<
mSlowest
)
{
mSlowest
=
*
si
;
}
}
// We're especially interested in memory as framerate drops. Only log
// when framerate drops below the slowest framerate we remember.
// (Should always be true for the end of the very first sample
// window.)
if
(
framerate
>=
slowest
)
{
return
false
;
}
// Congratulations, we've hit a new low. :-P
LL_INFOS
(
"FrameWatcher"
)
<<
' '
;
if
(
!
prevSize
)
{
LL_CONT
<<
"initial framerate "
;
}
else
{
LL_CONT
<<
"slowest framerate for last "
<<
int
(
prevSize
*
MEM_INFO_THROTTLE
)
<<
" seconds "
;
}
LL_CONT
<<
std
::
fixed
<<
std
::
setprecision
(
1
)
<<
framerate
<<
'\n'
<<
LLMemoryInfo
()
<<
LL_ENDL
;
return
false
;
}
private
:
// Storing the connection in an LLTempBoundListener ensures it will be
// disconnected when we're destroyed.
LLTempBoundListener
mConnection
;
// Track elapsed time
LLTimer
mTimer
;
// Some of what you see here is in fact redundant with functionality you
// can get from LLTimer. Unfortunately the LLTimer API is missing the
// feature we need: has at least the stated interval elapsed, and if so,
// exactly how long has passed? So we have to do it by hand, sigh.
// Time at start, end of sample window
F32
mSampleStart
,
mSampleEnd
;
// Frames this sample window
U32
mFrames
;
// Sliding window of framerate samples
boost
::
circular_buffer
<
F32
>
mSamples
;
// Slowest framerate in mSamples
F32
mSlowest
;
};
// Need an instance of FrameWatcher before it does any good
static
FrameWatcher
sFrameWatcher
;
BOOL
gunzip_file
(
const
std
::
string
&
srcfile
,
const
std
::
string
&
dstfile
)
BOOL
gunzip_file
(
const
std
::
string
&
srcfile
,
const
std
::
string
&
dstfile
)
{
{
std
::
string
tmpfile
;
std
::
string
tmpfile
;
...
...
This diff is collapsed.
Click to expand it.
indra/llcommon/llsys.h
+
22
−
0
View file @
fc5aaf5d
...
@@ -36,6 +36,7 @@
...
@@ -36,6 +36,7 @@
// llinfos << info << llendl;
// llinfos << info << llendl;
//
//
#include
"llsd.h"
#include
<iosfwd>
#include
<iosfwd>
#include
<string>
#include
<string>
...
@@ -117,6 +118,27 @@ class LL_COMMON_API LLMemoryInfo
...
@@ -117,6 +118,27 @@ class LL_COMMON_API LLMemoryInfo
//get the available memory infomation in KiloBytes.
//get the available memory infomation in KiloBytes.
static
void
getAvailableMemoryKB
(
U32
&
avail_physical_mem_kb
,
U32
&
avail_virtual_mem_kb
);
static
void
getAvailableMemoryKB
(
U32
&
avail_physical_mem_kb
,
U32
&
avail_virtual_mem_kb
);
// Retrieve a map of memory statistics. The keys of the map are platform-
// dependent. The values are in kilobytes to try to avoid integer overflow.
LLSD
getStatsMap
()
const
;
// Re-fetch memory data (as reported by stream() and getStatsMap()) from the
// system. Normally this is fetched at construction time. Return (*this)
// to permit usage of the form:
// @code
// LLMemoryInfo info;
// ...
// info.refresh().getStatsMap();
// @endcode
LLMemoryInfo
&
refresh
();
private:
// set mStatsMap
static
LLSD
loadStatsMap
();
// Memory stats for getStatsMap().
LLSD
mStatsMap
;
};
};
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment