diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp
index 289ce0bc2cfda4b52a63975e7d1905e47d03210b..694c90e1846f524e50077b1fcf4424c6225fc763 100644
--- a/indra/llcommon/llfile.cpp
+++ b/indra/llcommon/llfile.cpp
@@ -92,6 +92,17 @@ LLFILE*	LLFile::_fsopen(const std::string& filename, const char* mode, int shari
 #endif
 }
 
+int	LLFile::close(LLFILE * file)
+{
+	int ret_value = 0;
+	if (file)
+	{
+		ret_value = fclose(file);
+	}
+	return ret_value;
+}
+
+
 int	LLFile::remove(const std::string& filename)
 {
 #if	LL_WINDOWS
diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h
index 4913af7cb54dc52d09b81c2b67a6180e0673db90..dd7d36513a06d13c81638c1d7eafbdac2e60ab9e 100644
--- a/indra/llcommon/llfile.h
+++ b/indra/llcommon/llfile.h
@@ -71,6 +71,8 @@ class LL_COMMON_API LLFile
 	static	LLFILE*	fopen(const std::string& filename,const char* accessmode);	/* Flawfinder: ignore */
 	static	LLFILE*	_fsopen(const std::string& filename,const char* accessmode,int	sharingFlag);
 
+	static	int		close(LLFILE * file);
+
 	// perms is a permissions mask like 0777 or 0700.  In most cases it will
 	// be overridden by the user's umask.  It is ignored on Windows.
 	static	int		mkdir(const std::string& filename, int perms = 0700);
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 3a4b9be0d7393c4def4188c5af5178130421798b..7724f41931e46db971aaac766eb24bbf0e90bbe9 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -523,6 +523,7 @@ set(viewer_SOURCE_FILES
     llviewerregion.cpp
     llviewershadermgr.cpp
     llviewerstats.cpp
+    llviewerstatsrecorder.cpp
     llviewertexteditor.cpp
     llviewertexture.cpp
     llviewertextureanim.cpp
@@ -1051,6 +1052,7 @@ set(viewer_HEADER_FILES
     llviewerregion.h
     llviewershadermgr.h
     llviewerstats.h
+    llviewerstatsrecorder.h
     llviewertexteditor.h
     llviewertexture.h
     llviewertextureanim.h
diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp
index f5a32438cfef2effc43e3372208c528b924b9e0a..d14fd693488a488cf03378fa248b0e21664c438c 100644
--- a/indra/newview/llviewerobjectlist.cpp
+++ b/indra/newview/llviewerobjectlist.cpp
@@ -56,6 +56,7 @@
 #include "llresmgr.h"
 #include "llviewerregion.h"
 #include "llviewerstats.h"
+#include "llviewerstatsrecorder.h"
 #include "llvoavatarself.h"
 #include "lltoolmgr.h"
 #include "lltoolpie.h"
@@ -345,7 +346,13 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
 	U8 compressed_dpbuffer[2048];
 	LLDataPackerBinaryBuffer compressed_dp(compressed_dpbuffer, 2048);
 	LLDataPacker *cached_dpp = NULL;
-	
+
+#if LL_RECORD_VIEWER_STATS
+	static LLViewerStatsRecorder	stats_recorder;
+	stats_recorder.initStatsRecorder(regionp);
+	stats_recorder.initCachedObjectUpdate(regionp);
+#endif
+
 	for (i = 0; i < num_objects; i++)
 	{
 		LLTimer update_timer;
@@ -369,6 +376,10 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
 			}
 			else
 			{
+				#if LL_RECORD_VIEWER_STATS
+				stats_recorder.recordCachedObjectEvent(regionp, id, NULL);
+				#endif
+
 				continue; // no data packer, skip this object
 			}
 		}
@@ -540,6 +551,10 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
 		{
 			objectp->mLocalID = local_id;
 			processUpdateCore(objectp, user_data, i, update_type, cached_dpp, justCreated);
+
+			#if LL_RECORD_VIEWER_STATS
+			stats_recorder.recordCachedObjectEvent(regionp, local_id, objectp);
+			#endif
 		}
 		else
 		{
@@ -551,6 +566,10 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
 		}
 	}
 
+#if LL_RECORD_VIEWER_STATS
+	stats_recorder.closeCachedObjectUpdate(regionp);
+#endif
+
 	LLVOAvatar::cullAvatarsByPixelArea();
 }
 
diff --git a/indra/newview/llviewerstatsrecorder.cpp b/indra/newview/llviewerstatsrecorder.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4e7cf00ba00e760b79d23936c334fae540362077
--- /dev/null
+++ b/indra/newview/llviewerstatsrecorder.cpp
@@ -0,0 +1,121 @@
+/**
+ * @file llviewerstatsrecorder.cpp
+ * @brief record info about viewer events to a metrics log file
+ *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library 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
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llviewerstatsrecorder.h"
+#include "llfile.h"
+#include "llviewerregion.h"
+#include "llviewerobject.h"
+
+LLViewerStatsRecorder::LLViewerStatsRecorder() :
+	mObjectCacheFile(NULL),
+	mTimer()
+{
+	mStartTime = LLTimer::getTotalTime();
+}
+
+LLViewerStatsRecorder::~LLViewerStatsRecorder()
+{
+	LLFile::close(mObjectCacheFile);
+	mObjectCacheFile = NULL;
+}
+
+
+void LLViewerStatsRecorder::initStatsRecorder(LLViewerRegion *regionp)
+{
+	// To do - something using region name or global position
+#if LL_WINDOWS
+	std::string stats_file_name("C:\\ViewerObjectCacheStats.csv");
+#else
+	std::string stats_file_name("~/viewerstats.csv");
+#endif
+
+	if (mObjectCacheFile == NULL)
+	{
+		mObjectCacheFile = LLFile::fopen(stats_file_name, "wb");
+		if (mObjectCacheFile)
+		{	// Write column headers
+			std::ostringstream data_msg;
+			data_msg << "Time, "
+				<< "Hits, "
+				<< "Misses, "
+				<< "Objects "
+				<< "\n";
+
+			fwrite(data_msg.str().c_str(), 1, data_msg.str().size(), mObjectCacheFile );
+		}
+	}
+}
+
+
+void LLViewerStatsRecorder::initCachedObjectUpdate(LLViewerRegion *regionp)
+{
+	mObjectCacheHitCount = 0;
+	mObjectCacheMissCount = 0;
+}
+
+
+void LLViewerStatsRecorder::recordCachedObjectEvent(LLViewerRegion *regionp, U32 local_id, LLViewerObject * objectp)
+{
+	if (objectp)
+	{
+		mObjectCacheHitCount++;
+	}
+	else
+	{	// no object, must be a miss
+		mObjectCacheMissCount++;
+	}
+}
+
+void LLViewerStatsRecorder::closeCachedObjectUpdate(LLViewerRegion *regionp)
+{
+	llinfos << "ILX: " << mObjectCacheHitCount 
+		<< " hits " 
+		<< mObjectCacheMissCount << " misses"
+		<< llendl;
+
+	S32 total_objects = mObjectCacheHitCount + mObjectCacheMissCount;
+	if (mObjectCacheFile != NULL &&
+		total_objects > 0)
+	{
+		std::ostringstream data_msg;
+		F32 now32 = (F32) ((LLTimer::getTotalTime() - mStartTime) / 1000.0);
+
+		data_msg << now32
+			<< ", " << mObjectCacheHitCount
+			<< ", " << mObjectCacheMissCount
+			<< ", " << total_objects
+			<< "\n";
+
+		fwrite(data_msg.str().c_str(), 1, data_msg.str().size(), mObjectCacheFile );
+	}
+
+	mObjectCacheHitCount = 0;
+	mObjectCacheMissCount = 0;
+}
+
+
+
diff --git a/indra/newview/llviewerstatsrecorder.h b/indra/newview/llviewerstatsrecorder.h
new file mode 100644
index 0000000000000000000000000000000000000000..0c5e6d50101da2be267bcf84f2b5596e10ef5fee
--- /dev/null
+++ b/indra/newview/llviewerstatsrecorder.h
@@ -0,0 +1,66 @@
+/**
+ * @file llviewerstatsrecorder.h
+ * @brief record info about viewer events to a metrics log file
+ *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library 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
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LLVIEWERSTATSRECORDER_H
+#define LLVIEWERSTATSRECORDER_H
+
+
+// This is a diagnostic class used to record information from the viewer
+// for analysis.
+
+// This is normally 0.  Set to 1 to enable viewer stats recording
+#define LL_RECORD_VIEWER_STATS	1
+
+
+#if LL_RECORD_VIEWER_STATS
+#include "llframetimer.h"
+
+class LLViewerRegion;
+class LLViewerObject;
+
+class LLViewerStatsRecorder
+{
+ public:
+	LLViewerStatsRecorder();
+	~LLViewerStatsRecorder();
+
+	void initStatsRecorder(LLViewerRegion *regionp);
+
+	void initCachedObjectUpdate(LLViewerRegion *regionp);
+	void recordCachedObjectEvent(LLViewerRegion *regionp, U32 local_id, LLViewerObject * objectp);
+	void closeCachedObjectUpdate(LLViewerRegion *regionp);
+
+private:
+	 LLFrameTimer	mTimer;
+	 F64			mStartTime;
+
+	 LLFILE *		mObjectCacheFile;		// File to write data into
+	 S32			mObjectCacheHitCount;
+	 S32			mObjectCacheMissCount;
+};
+#endif	// LL_RECORD_VIEWER_STATS
+
+#endif // LLVIEWERSTATSRECORDER_H