diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 4336550d07141ec774af6d1a0d63234e20ab260d..62880b07f638987f993534f2e996dd5a9b1ec13a 100755
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -287,6 +287,7 @@ if (LL_TESTS)
   LL_ADD_INTEGRATION_TEST(llsdserialize "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(llsingleton "" "${test_libs}")                          
   LL_ADD_INTEGRATION_TEST(llstring "" "${test_libs}")
+  LL_ADD_INTEGRATION_TEST(lltrace "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(lltreeiterators "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(lluri "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(llunits "" "${test_libs}")
diff --git a/indra/llcommon/lltraceaccumulators.cpp b/indra/llcommon/lltraceaccumulators.cpp
index 5e25ad6b263d9eb6a5ac7143afd49565651f5bfb..42f075a7cb6f3e6ca422517f9bfba760199334ee 100644
--- a/indra/llcommon/lltraceaccumulators.cpp
+++ b/indra/llcommon/lltraceaccumulators.cpp
@@ -192,7 +192,7 @@ void SampleAccumulator::reset( const SampleAccumulator* other )
 	mMin = mLastValue;
 	mMax = mLastValue;
 	mMean = mLastValue;
-	LL_ERRS_IF(mHasValue && !(mMean < 0) && !(mMean >= 0)) << "Invalid mean after capturing value" << LL_ENDL;
+	llassert(!mHasValue || mMean < 0 || mMean >= 0);
 	mSumOfSquares = 0;
 	mLastSampleTimeStamp = LLTimer::getTotalSeconds();
 	mTotalSamplingTime = 0;
diff --git a/indra/llcommon/lltraceaccumulators.h b/indra/llcommon/lltraceaccumulators.h
index d4ff4b8d7106a65d2f1bbc13692bc68ccc058dfd..bf195f72b14fcd55238c004f8c9625bbfa532d0a 100644
--- a/indra/llcommon/lltraceaccumulators.h
+++ b/indra/llcommon/lltraceaccumulators.h
@@ -342,6 +342,7 @@ namespace LLTrace
 				mSum += mLastValue * delta_time;
 				mTotalSamplingTime += delta_time;
 				F64 old_mean = mMean;
+				llassert(mMean < 0 || mMean >= 0);
 				mMean += (delta_time / mTotalSamplingTime) * (mLastValue - old_mean);
 				llassert(mMean < 0 || mMean >= 0);
 				mSumOfSquares += delta_time * (mLastValue - old_mean) * (mLastValue - mMean);
diff --git a/indra/llcommon/llunit.h b/indra/llcommon/llunit.h
index bfc011bb55edc7dc80ca84f2b0f4f677da864e76..de9cee33fd84d028db030d8ea0f1e14c184acd56 100644
--- a/indra/llcommon/llunit.h
+++ b/indra/llcommon/llunit.h
@@ -601,9 +601,6 @@ struct LLUnitLinearOps
 		mDivisor(1)
 	{}
 
-	T mValue;
-	T mDivisor;
-
 	template<typename OTHER_T>
 	self_t operator * (OTHER_T other)
 	{
@@ -620,6 +617,7 @@ struct LLUnitLinearOps
 	template<typename OTHER_T>
 	self_t operator + (OTHER_T other)
 	{
+		mValue /= mDivisor;
 		mValue += other;
 		return *this;
 	}
@@ -627,9 +625,13 @@ struct LLUnitLinearOps
 	template<typename OTHER_T>
 	self_t operator - (OTHER_T other)
 	{
+		mValue /= mDivisor;
 		mValue -= other;
 		return *this;
 	}
+
+	T mValue;
+	T mDivisor;
 };
 
 template<typename T>
@@ -642,9 +644,6 @@ struct LLUnitInverseLinearOps
 		mDivisor(1)
 	{}
 
-	T mValue;
-	T mDivisor;
-
 	template<typename OTHER_T>
 	self_t operator * (OTHER_T other)
 	{
@@ -662,6 +661,7 @@ struct LLUnitInverseLinearOps
 	template<typename OTHER_T>
 	self_t operator + (OTHER_T other)
 	{
+		mValue /= mDivisor;
 		mValue -= other;
 		return *this;
 	}
@@ -669,9 +669,13 @@ struct LLUnitInverseLinearOps
 	template<typename OTHER_T>
 	self_t operator - (OTHER_T other)
 	{
+		mValue /= mDivisor;
 		mValue += other;
 		return *this;
 	}
+
+	T mValue;
+	T mDivisor;
 };
 
 #define LL_DECLARE_BASE_UNIT(base_unit_name, unit_label)                                             \
diff --git a/indra/llcommon/tests/lltrace_test.cpp b/indra/llcommon/tests/lltrace_test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1c2a4528ae25a5b994a57a6e130849100aac0abd
--- /dev/null
+++ b/indra/llcommon/tests/lltrace_test.cpp
@@ -0,0 +1,141 @@
+/** 
+ * @file llsingleton_test.cpp
+ * @date 2011-08-11
+ * @brief Unit test for the LLSingleton class
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, 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 "linden_common.h"
+
+#include "lltrace.h"
+#include "lltracethreadrecorder.h"
+#include "lltracerecording.h"
+#include "../test/lltut.h"
+
+namespace LLUnits
+{
+	// using powers of 2 to allow strict floating point equality
+	LL_DECLARE_BASE_UNIT(Ounces, "oz");
+	LL_DECLARE_DERIVED_UNIT(Ounces, * 12, TallCup, "");
+	LL_DECLARE_DERIVED_UNIT(Ounces, * 16, GrandeCup, "");
+	LL_DECLARE_DERIVED_UNIT(Ounces, * 20, VentiCup, "");
+
+	LL_DECLARE_BASE_UNIT(Grams, "g");
+	LL_DECLARE_DERIVED_UNIT(Grams, / 1000, Milligrams, "mg");
+}
+
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Ounces);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, TallCup);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, GrandeCup);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, VentiCup);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Grams);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Milligrams);
+
+
+namespace tut
+{
+	using namespace LLTrace;
+	struct trace
+	{
+		ThreadRecorder mRecorder;
+	};
+
+	typedef test_group<trace> trace_t;
+	typedef trace_t::object trace_object_t;
+	tut::trace_t tut_singleton("LLTrace");
+
+	static CountStatHandle<S32> sCupsOfCoffeeConsumed("coffeeconsumed", "Delicious cup of dark roast.");
+	static SampleStatHandle<F32Milligrams> sCaffeineLevelStat("caffeinelevel", "Coffee buzz quotient");
+	static EventStatHandle<S32Ounces> sOuncesPerCup("cupsize", "Large, huge, or ginormous");
+
+	static F32 sCaffeineLevel(0.f);
+	const F32Milligrams sCaffeinePerOz(18.f);
+
+	void drink_coffee(S32 num_cups, S32Ounces cup_size)
+	{
+		add(sCupsOfCoffeeConsumed, num_cups);
+		for (S32 i = 0; i < num_cups; i++)
+		{
+			record(sOuncesPerCup, cup_size);
+		}
+
+		sCaffeineLevel += F32Ounces(num_cups * cup_size).value() * sCaffeinePerOz.value();
+		sample(sCaffeineLevelStat, sCaffeineLevel);
+	}
+
+	// basic data collection
+	template<> template<>
+	void trace_object_t::test<1>()
+	{
+		sample(sCaffeineLevelStat, sCaffeineLevel);
+
+		Recording all_day;
+		Recording at_work;
+		Recording after_3pm;
+
+		all_day.start();
+		{
+			// warm up with one grande cup
+			drink_coffee(1, S32TallCup(1));
+
+			// go to work
+			at_work.start();
+			{
+				// drink 3 tall cups, 1 after 3 pm
+				drink_coffee(2, S32GrandeCup(1));
+				after_3pm.start();
+				drink_coffee(1, S32GrandeCup(1));
+			}
+			at_work.stop();
+			drink_coffee(1, S32VentiCup(1));
+		}
+		after_3pm.stop();
+		all_day.stop();
+
+		ensure("count stats are counted when recording is active", 
+			at_work.getSum(sCupsOfCoffeeConsumed) == 3 
+				&& all_day.getSum(sCupsOfCoffeeConsumed) == 5
+				&& after_3pm.getSum(sCupsOfCoffeeConsumed) == 2);
+		ensure("measurement sums are counted when recording is active", 
+			at_work.getSum(sOuncesPerCup) == S32Ounces(48) 
+				&& all_day.getSum(sOuncesPerCup) == S32Ounces(80)
+				&& after_3pm.getSum(sOuncesPerCup) == S32Ounces(36));
+		ensure("measurement min is specific to when recording is active", 
+			at_work.getMin(sOuncesPerCup) == S32GrandeCup(1) 
+				&& all_day.getMin(sOuncesPerCup) == S32TallCup(1)
+				&& after_3pm.getMin(sOuncesPerCup) == S32GrandeCup(1));
+		ensure("measurement max is specific to when recording is active", 
+			at_work.getMax(sOuncesPerCup) == S32GrandeCup(1) 
+				&& all_day.getMax(sOuncesPerCup) == S32VentiCup(1)
+				&& after_3pm.getMax(sOuncesPerCup) == S32VentiCup(1));
+		ensure("sample min is specific to when recording is active", 
+			at_work.getMin(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1)).value()
+				&& all_day.getMin(sCaffeineLevelStat) == F32Milligrams(0.f)
+				&& after_3pm.getMin(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(2)).value());
+		ensure("sample max is specific to when recording is active", 
+			at_work.getMax(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(3)).value()
+				&& all_day.getMax(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(3) + (S32Ounces)S32VentiCup(1)).value()
+				&& after_3pm.getMax(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(3) + (S32Ounces)S32VentiCup(1)).value());
+	}
+
+}
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index f506765da3e1c8b5eef1a9b8ce099b5a516ba0b8..0f155b8ad718253d2f78b5a56250ffbb1014b987 100755
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -1463,7 +1463,7 @@ bool LLAppViewer::mainLoop()
 					ms_sleep(500);
 				}
 
-				const F64 max_idle_time = llmin(.005f*10.f*(F32MillisecondsImplicit)gFrameTimeSeconds, F32MillisecondsImplicit(5)); // 5 ms a second
+				const F64Milliseconds max_idle_time = llmin(.005f*10.f*(F32Milliseconds)gFrameTimeSeconds, F32Milliseconds(5)); // 5 ms a second
 				idleTimer.reset();
 				S32 total_work_pending = 0;
 				S32 total_io_pending = 0;