summaryrefslogtreecommitdiffstats
path: root/runtime/base
diff options
context:
space:
mode:
authorBaligh Uddin <baligh@google.com>2013-11-01 15:34:23 -0700
committerBaligh Uddin <baligh@google.com>2013-11-01 15:34:23 -0700
commitfa91a0705ce6651a3f3f20dd55a86842eea65c2e (patch)
treef6182da0b1d99645f7ead50df30ebf8448ad0164 /runtime/base
parent1eeba46ba66e462b58640b604b035a481d65d898 (diff)
parent56cbb5872a5b48815311f2c6a1a3292682a56afa (diff)
downloadart-fa91a0705ce6651a3f3f20dd55a86842eea65c2e.zip
art-fa91a0705ce6651a3f3f20dd55a86842eea65c2e.tar.gz
art-fa91a0705ce6651a3f3f20dd55a86842eea65c2e.tar.bz2
Merge remote-tracking branch 'origin/kitkat-dev'
Diffstat (limited to 'runtime/base')
-rw-r--r--runtime/base/bounded_fifo.h74
-rw-r--r--runtime/base/casts.h93
-rw-r--r--runtime/base/histogram-inl.h231
-rw-r--r--runtime/base/histogram.h117
-rw-r--r--runtime/base/histogram_test.cc270
-rw-r--r--runtime/base/logging.cc247
-rw-r--r--runtime/base/logging.h336
-rw-r--r--runtime/base/logging_android.cc43
-rw-r--r--runtime/base/logging_linux.cc38
-rw-r--r--runtime/base/macros.h203
-rw-r--r--runtime/base/mutex-inl.h190
-rw-r--r--runtime/base/mutex.cc920
-rw-r--r--runtime/base/mutex.h418
-rw-r--r--runtime/base/mutex_test.cc173
-rw-r--r--runtime/base/stl_util.h97
-rw-r--r--runtime/base/stringpiece.cc95
-rw-r--r--runtime/base/stringpiece.h213
-rw-r--r--runtime/base/stringprintf.cc81
-rw-r--r--runtime/base/stringprintf.h38
-rw-r--r--runtime/base/timing_logger.cc292
-rw-r--r--runtime/base/timing_logger.h167
-rw-r--r--runtime/base/timing_logger_test.cc160
-rw-r--r--runtime/base/unix_file/README15
-rw-r--r--runtime/base/unix_file/fd_file.cc135
-rw-r--r--runtime/base/unix_file/fd_file.h75
-rw-r--r--runtime/base/unix_file/fd_file_test.cc63
-rw-r--r--runtime/base/unix_file/mapped_file.cc162
-rw-r--r--runtime/base/unix_file/mapped_file.h97
-rw-r--r--runtime/base/unix_file/mapped_file_test.cc265
-rw-r--r--runtime/base/unix_file/null_file.cc61
-rw-r--r--runtime/base/unix_file/null_file.h50
-rw-r--r--runtime/base/unix_file/null_file_test.cc67
-rw-r--r--runtime/base/unix_file/random_access_file.h68
-rw-r--r--runtime/base/unix_file/random_access_file_test.h172
-rw-r--r--runtime/base/unix_file/random_access_file_utils.cc38
-rw-r--r--runtime/base/unix_file/random_access_file_utils.h30
-rw-r--r--runtime/base/unix_file/random_access_file_utils_test.cc56
-rw-r--r--runtime/base/unix_file/string_file.cc98
-rw-r--r--runtime/base/unix_file/string_file.h59
-rw-r--r--runtime/base/unix_file/string_file_test.cc42
40 files changed, 6049 insertions, 0 deletions
diff --git a/runtime/base/bounded_fifo.h b/runtime/base/bounded_fifo.h
new file mode 100644
index 0000000..cb92d40
--- /dev/null
+++ b/runtime/base/bounded_fifo.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_BASE_BOUNDED_FIFO_H_
+#define ART_RUNTIME_BASE_BOUNDED_FIFO_H_
+
+#include "cutils/atomic.h"
+#include "cutils/atomic-inline.h"
+
+namespace art {
+
+// A bounded fifo is a fifo which has a bounded size. The power of two version uses a bit mask to
+// avoid needing to deal with wrapping integers around or using a modulo operation.
+template <typename T, const size_t MaxSize>
+class BoundedFifoPowerOfTwo {
+ public:
+ BoundedFifoPowerOfTwo() {
+ // TODO: Do this with a compile time check.
+ CHECK(IsPowerOfTwo(MaxSize));
+ clear();
+ }
+
+ void clear() {
+ back_index_ = 0;
+ size_ = 0;
+ }
+
+ bool empty() const {
+ return size() == 0;
+ }
+
+ size_t size() const {
+ return size_;
+ }
+
+ void push_back(const T& value) {
+ ++size_;
+ DCHECK_LE(size_, MaxSize);
+ // Relies on integer overflow behaviour.
+ data_[back_index_++ & mask_] = value;
+ }
+
+ const T& front() const {
+ DCHECK_GT(size_, 0U);
+ return data_[(back_index_ - size_) & mask_];
+ }
+
+ void pop_front() {
+ DCHECK_GT(size_, 0U);
+ --size_;
+ }
+
+ private:
+ static const size_t mask_ = MaxSize - 1;
+ size_t back_index_, size_;
+ T data_[MaxSize];
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_BASE_BOUNDED_FIFO_H_
diff --git a/runtime/base/casts.h b/runtime/base/casts.h
new file mode 100644
index 0000000..be94c2e
--- /dev/null
+++ b/runtime/base/casts.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_BASE_CASTS_H_
+#define ART_RUNTIME_BASE_CASTS_H_
+
+#include <assert.h>
+#include <string.h>
+#include "base/macros.h"
+
+namespace art {
+
+// Use implicit_cast as a safe version of static_cast or const_cast
+// for upcasting in the type hierarchy (i.e. casting a pointer to Foo
+// to a pointer to SuperclassOfFoo or casting a pointer to Foo to
+// a const pointer to Foo).
+// When you use implicit_cast, the compiler checks that the cast is safe.
+// Such explicit implicit_casts are necessary in surprisingly many
+// situations where C++ demands an exact type match instead of an
+// argument type convertable to a target type.
+//
+// The From type can be inferred, so the preferred syntax for using
+// implicit_cast is the same as for static_cast etc.:
+//
+// implicit_cast<ToType>(expr)
+//
+// implicit_cast would have been part of the C++ standard library,
+// but the proposal was submitted too late. It will probably make
+// its way into the language in the future.
+template<typename To, typename From>
+inline To implicit_cast(From const &f) {
+ return f;
+}
+
+// When you upcast (that is, cast a pointer from type Foo to type
+// SuperclassOfFoo), it's fine to use implicit_cast<>, since upcasts
+// always succeed. When you downcast (that is, cast a pointer from
+// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because
+// how do you know the pointer is really of type SubclassOfFoo? It
+// could be a bare Foo, or of type DifferentSubclassOfFoo. Thus,
+// when you downcast, you should use this macro. In debug mode, we
+// use dynamic_cast<> to double-check the downcast is legal (we die
+// if it's not). In normal mode, we do the efficient static_cast<>
+// instead. Thus, it's important to test in debug mode to make sure
+// the cast is legal!
+// This is the only place in the code we should use dynamic_cast<>.
+// In particular, you SHOULDN'T be using dynamic_cast<> in order to
+// do RTTI (eg code like this:
+// if (dynamic_cast<Subclass1>(foo)) HandleASubclass1Object(foo);
+// if (dynamic_cast<Subclass2>(foo)) HandleASubclass2Object(foo);
+// You should design the code some other way not to need this.
+
+template<typename To, typename From> // use like this: down_cast<T*>(foo);
+inline To down_cast(From* f) { // so we only accept pointers
+ // Ensures that To is a sub-type of From *. This test is here only
+ // for compile-time type checking, and has no overhead in an
+ // optimized build at run-time, as it will be optimized away
+ // completely.
+ if (false) {
+ implicit_cast<From*, To>(0);
+ }
+
+ //
+ // assert(f == NULL || dynamic_cast<To>(f) != NULL); // RTTI: debug mode only!
+ return static_cast<To>(f);
+}
+
+template <class Dest, class Source>
+inline Dest bit_cast(const Source& source) {
+ // Compile time assertion: sizeof(Dest) == sizeof(Source)
+ // A compile error here means your Dest and Source have different sizes.
+ COMPILE_ASSERT(sizeof(Dest) == sizeof(Source), verify_sizes_are_equal);
+ Dest dest;
+ memcpy(&dest, &source, sizeof(dest));
+ return dest;
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_BASE_CASTS_H_
diff --git a/runtime/base/histogram-inl.h b/runtime/base/histogram-inl.h
new file mode 100644
index 0000000..0345266
--- /dev/null
+++ b/runtime/base/histogram-inl.h
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_BASE_HISTOGRAM_INL_H_
+#define ART_RUNTIME_BASE_HISTOGRAM_INL_H_
+
+#include "histogram.h"
+
+#include "utils.h"
+
+#include <algorithm>
+#include <cmath>
+#include <limits>
+#include <ostream>
+
+namespace art {
+
+template <class Value> inline void Histogram<Value>::AddValue(Value value) {
+ CHECK_GE(value, static_cast<Value>(0));
+ if (value >= max_) {
+ Value new_max = ((value + 1) / bucket_width_ + 1) * bucket_width_;
+ DCHECK_GT(new_max, max_);
+ GrowBuckets(new_max);
+ }
+
+ BucketiseValue(value);
+}
+
+template <class Value>
+inline Histogram<Value>::Histogram(const char* name, Value initial_bucket_width,
+ size_t max_buckets)
+ : kAdjust(1000),
+ kInitialBucketCount(8),
+ name_(name),
+ max_buckets_(max_buckets),
+ bucket_width_(initial_bucket_width) {
+ Reset();
+}
+
+template <class Value>
+inline void Histogram<Value>::GrowBuckets(Value new_max) {
+ while (max_ < new_max) {
+ // If we have reached the maximum number of buckets, merge buckets together.
+ if (frequency_.size() >= max_buckets_) {
+ CHECK(IsAligned<2>(frequency_.size()));
+ // We double the width of each bucket to reduce the number of buckets by a factor of 2.
+ bucket_width_ *= 2;
+ const size_t limit = frequency_.size() / 2;
+ // Merge the frequencies by adding each adjacent two together.
+ for (size_t i = 0; i < limit; ++i) {
+ frequency_[i] = frequency_[i * 2] + frequency_[i * 2 + 1];
+ }
+ // Remove frequencies in the second half of the array which were added to the first half.
+ while (frequency_.size() > limit) {
+ frequency_.pop_back();
+ }
+ }
+ max_ += bucket_width_;
+ frequency_.push_back(0);
+ }
+}
+
+template <class Value> inline size_t Histogram<Value>::FindBucket(Value val) const {
+ // Since this is only a linear histogram, bucket index can be found simply with
+ // dividing the value by the bucket width.
+ DCHECK_GE(val, min_);
+ DCHECK_LE(val, max_);
+ const size_t bucket_idx = static_cast<size_t>((val - min_) / bucket_width_);
+ DCHECK_GE(bucket_idx, 0ul);
+ DCHECK_LE(bucket_idx, GetBucketCount());
+ return bucket_idx;
+}
+
+template <class Value>
+inline void Histogram<Value>::BucketiseValue(Value val) {
+ CHECK_LT(val, max_);
+ sum_ += val;
+ sum_of_squares_ += val * val;
+ ++sample_size_;
+ ++frequency_[FindBucket(val)];
+ max_value_added_ = std::max(val, max_value_added_);
+ min_value_added_ = std::min(val, min_value_added_);
+}
+
+template <class Value> inline void Histogram<Value>::Initialize() {
+ for (size_t idx = 0; idx < kInitialBucketCount; idx++) {
+ frequency_.push_back(0);
+ }
+ // Cumulative frequency and ranges has a length of 1 over frequency.
+ max_ = bucket_width_ * GetBucketCount();
+}
+
+template <class Value> inline size_t Histogram<Value>::GetBucketCount() const {
+ return frequency_.size();
+}
+
+template <class Value> inline void Histogram<Value>::Reset() {
+ sum_of_squares_ = 0;
+ sample_size_ = 0;
+ min_ = 0;
+ sum_ = 0;
+ min_value_added_ = std::numeric_limits<Value>::max();
+ max_value_added_ = std::numeric_limits<Value>::min();
+ frequency_.clear();
+ Initialize();
+}
+
+template <class Value> inline Value Histogram<Value>::GetRange(size_t bucket_idx) const {
+ DCHECK_LE(bucket_idx, GetBucketCount());
+ return min_ + bucket_idx * bucket_width_;
+}
+
+template <class Value> inline double Histogram<Value>::Mean() const {
+ DCHECK_GT(sample_size_, 0ull);
+ return static_cast<double>(sum_) / static_cast<double>(sample_size_);
+}
+
+template <class Value> inline double Histogram<Value>::Variance() const {
+ DCHECK_GT(sample_size_, 0ull);
+ // Using algorithms for calculating variance over a population:
+ // http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
+ Value sum_squared = sum_ * sum_;
+ double sum_squared_by_n_squared =
+ static_cast<double>(sum_squared) /
+ static_cast<double>(sample_size_ * sample_size_);
+ double sum_of_squares_by_n =
+ static_cast<double>(sum_of_squares_) / static_cast<double>(sample_size_);
+ return sum_of_squares_by_n - sum_squared_by_n_squared;
+}
+
+template <class Value>
+inline void Histogram<Value>::PrintBins(std::ostream& os, const CumulativeData& data) const {
+ DCHECK_GT(sample_size_, 0ull);
+ for (size_t bin_idx = 0; bin_idx < data.freq_.size(); ++bin_idx) {
+ if (bin_idx > 0 && data.perc_[bin_idx] == data.perc_[bin_idx - 1]) {
+ bin_idx++;
+ continue;
+ }
+ os << GetRange(bin_idx) << ": " << data.freq_[bin_idx] << "\t"
+ << data.perc_[bin_idx] * 100.0 << "%\n";
+ }
+}
+
+template <class Value>
+inline void Histogram<Value>::PrintConfidenceIntervals(std::ostream &os, double interval,
+ const CumulativeData& data) const {
+ DCHECK_GT(interval, 0);
+ DCHECK_LT(interval, 1.0);
+
+ double per_0 = (1.0 - interval) / 2.0;
+ double per_1 = per_0 + interval;
+ os << Name() << ":\t";
+ TimeUnit unit = GetAppropriateTimeUnit(Mean() * kAdjust);
+ os << (interval * 100) << "% C.I. " << FormatDuration(Percentile(per_0, data) * kAdjust, unit);
+ os << "-" << FormatDuration(Percentile(per_1, data) * kAdjust, unit) << " ";
+ os << "Avg: " << FormatDuration(Mean() * kAdjust, unit) << " Max: ";
+ os << FormatDuration(Max() * kAdjust, unit) << "\n";
+}
+
+template <class Value> inline void Histogram<Value>::CreateHistogram(CumulativeData& out_data) {
+ DCHECK_GT(sample_size_, 0ull);
+ out_data.freq_.clear();
+ out_data.perc_.clear();
+ uint64_t accumulated = 0;
+ out_data.freq_.push_back(accumulated);
+ out_data.perc_.push_back(0.0);
+ for (size_t idx = 0; idx < frequency_.size(); idx++) {
+ accumulated += frequency_[idx];
+ out_data.freq_.push_back(accumulated);
+ out_data.perc_.push_back(static_cast<double>(accumulated) / static_cast<double>(sample_size_));
+ }
+ DCHECK_EQ(out_data.freq_.back(), sample_size_);
+ DCHECK_LE(std::abs(out_data.perc_.back() - 1.0), 0.001);
+}
+
+template <class Value>
+inline double Histogram<Value>::Percentile(double per, const CumulativeData& data) const {
+ DCHECK_GT(data.perc_.size(), 0ull);
+ size_t upper_idx = 0, lower_idx = 0;
+ for (size_t idx = 0; idx < data.perc_.size(); idx++) {
+ if (per <= data.perc_[idx]) {
+ upper_idx = idx;
+ break;
+ }
+
+ if (per >= data.perc_[idx] && idx != 0 && data.perc_[idx] != data.perc_[idx - 1]) {
+ lower_idx = idx;
+ }
+ }
+
+ const double lower_perc = data.perc_[lower_idx];
+ const double lower_value = static_cast<double>(GetRange(lower_idx));
+ if (per == lower_perc) {
+ return lower_value;
+ }
+
+ const double upper_perc = data.perc_[upper_idx];
+ const double upper_value = static_cast<double>(GetRange(upper_idx));
+ if (per == upper_perc) {
+ return upper_value;
+ }
+ DCHECK_GT(upper_perc, lower_perc);
+
+ double value = lower_value + (upper_value - lower_value) *
+ (per - lower_perc) / (upper_perc - lower_perc);
+
+ if (value < min_value_added_) {
+ value = min_value_added_;
+ } else if (value > max_value_added_) {
+ value = max_value_added_;
+ }
+
+ return value;
+}
+
+} // namespace art
+#endif // ART_RUNTIME_BASE_HISTOGRAM_INL_H_
+
diff --git a/runtime/base/histogram.h b/runtime/base/histogram.h
new file mode 100644
index 0000000..2a02cf4
--- /dev/null
+++ b/runtime/base/histogram.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ART_RUNTIME_BASE_HISTOGRAM_H_
+#define ART_RUNTIME_BASE_HISTOGRAM_H_
+
+#include <vector>
+#include <string>
+
+#include "base/logging.h"
+#include "utils.h"
+
+namespace art {
+
+// Creates a data histogram for a better understanding of statistical data.
+// Histogram analysis goes beyond simple mean and standard deviation to provide
+// percentiles values, describing where the $% of the input data lies.
+// Designed to be simple and used with timing logger in art.
+
+template <class Value> class Histogram {
+ const double kAdjust;
+ const size_t kInitialBucketCount;
+
+ public:
+ class CumulativeData {
+ friend class Histogram<Value>;
+ std::vector<uint64_t> freq_;
+ std::vector<double> perc_;
+ };
+
+ Histogram(const char* name, Value initial_bucket_width, size_t max_buckets = 100);
+ void AddValue(Value);
+ // Builds the cumulative distribution function from the frequency data.
+ // Accumulative summation of frequencies.
+ // cumulative_freq[i] = sum(frequency[j] : 0 < j < i )
+ // Accumulative summation of percentiles; which is the frequency / SampleSize
+ // cumulative_perc[i] = sum(frequency[j] / SampleSize : 0 < j < i )
+ void CreateHistogram(CumulativeData& data);
+ // Reset the cumulative values, next time CreateHistogram is called it will recreate the cache.
+ void Reset();
+ double Mean() const;
+ double Variance() const;
+ double Percentile(double per, const CumulativeData& data) const;
+ void PrintConfidenceIntervals(std::ostream& os, double interval,
+ const CumulativeData& data) const;
+ void PrintBins(std::ostream& os, const CumulativeData& data) const;
+ Value GetRange(size_t bucket_idx) const;
+ size_t GetBucketCount() const;
+
+ uint64_t SampleSize() const {
+ return sample_size_;
+ }
+
+ Value Sum() const {
+ return sum_;
+ }
+
+ Value Min() const {
+ return min_value_added_;
+ }
+
+ Value Max() const {
+ return max_value_added_;
+ }
+
+ const std::string& Name() const {
+ return name_;
+ }
+
+ private:
+ void Initialize();
+ size_t FindBucket(Value val) const;
+ void BucketiseValue(Value val);
+ // Add more buckets to the histogram to fill in a new value that exceeded
+ // the max_read_value_.
+ void GrowBuckets(Value val);
+ std::string name_;
+ // Maximum number of buckets.
+ const size_t max_buckets_;
+ // Number of samples placed in histogram.
+ size_t sample_size_;
+ // Width of the bucket range. The lower the value is the more accurate
+ // histogram percentiles are. Grows adaptively when we hit max buckets.
+ Value bucket_width_;
+ // How many occurrences of values fall within a bucket at index i where i covers the range
+ // starting at min_ + i * bucket_width_ with size bucket_size_.
+ std::vector<uint32_t> frequency_;
+ // Summation of all the elements inputed by the user.
+ Value sum_;
+ // Minimum value that can fit in the histogram. Fixed to zero for now.
+ Value min_;
+ // Maximum value that can fit in the histogram, grows adaptively.
+ Value max_;
+ // Summation of the values entered. Used to calculate variance.
+ Value sum_of_squares_;
+ // Maximum value entered in the histogram.
+ Value min_value_added_;
+ // Minimum value entered in the histogram.
+ Value max_value_added_;
+
+ DISALLOW_COPY_AND_ASSIGN(Histogram);
+};
+} // namespace art
+
+#endif // ART_RUNTIME_BASE_HISTOGRAM_H_
diff --git a/runtime/base/histogram_test.cc b/runtime/base/histogram_test.cc
new file mode 100644
index 0000000..534440c
--- /dev/null
+++ b/runtime/base/histogram_test.cc
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gtest/gtest.h"
+#include "histogram-inl.h"
+#include "UniquePtr.h"
+
+#include <sstream>
+
+namespace art {
+
+// Simple usage:
+// Histogram *hist(new Histogram("SimplePercentiles"));
+// Percentile PerValue
+// hist->AddValue(121);
+// hist->AddValue(132);
+// hist->AddValue(140);
+// hist->AddValue(145);
+// hist->AddValue(155);
+// hist->CreateHistogram();
+// PerValue = hist->PercentileVal(0.50); finds the 50th percentile(median).
+
+TEST(Histtest, MeanTest) {
+ UniquePtr<Histogram<uint64_t> > hist(new Histogram<uint64_t>("MeanTest", 5));
+
+ double mean;
+ for (size_t Idx = 0; Idx < 90; Idx++) {
+ hist->AddValue(static_cast<uint64_t>(50));
+ }
+ mean = hist->Mean();
+ EXPECT_EQ(mean, 50);
+ hist->Reset();
+ hist->AddValue(9);
+ hist->AddValue(17);
+ hist->AddValue(28);
+ hist->AddValue(28);
+ mean = hist->Mean();
+ EXPECT_EQ(20.5, mean);
+}
+
+TEST(Histtest, VarianceTest) {
+ UniquePtr<Histogram<uint64_t> > hist(new Histogram<uint64_t>("VarianceTest", 5));
+
+ double variance;
+ hist->AddValue(9);
+ hist->AddValue(17);
+ hist->AddValue(28);
+ hist->AddValue(28);
+ variance = hist->Variance();
+ EXPECT_EQ(64.25, variance);
+}
+
+TEST(Histtest, Percentile) {
+ UniquePtr<Histogram<uint64_t> > hist(new Histogram<uint64_t>("Percentile", 5));
+ Histogram<uint64_t>::CumulativeData data;
+
+ double PerValue;
+
+ hist->AddValue(20);
+ hist->AddValue(31);
+ hist->AddValue(42);
+ hist->AddValue(50);
+ hist->AddValue(60);
+ hist->AddValue(70);
+
+ hist->AddValue(98);
+
+ hist->AddValue(110);
+ hist->AddValue(121);
+ hist->AddValue(132);
+ hist->AddValue(140);
+ hist->AddValue(145);
+ hist->AddValue(155);
+
+ hist->CreateHistogram(data);
+ PerValue = hist->Percentile(0.50, data);
+ EXPECT_EQ(875, static_cast<int>(PerValue * 10));
+}
+
+TEST(Histtest, UpdateRange) {
+ UniquePtr<Histogram<uint64_t> > hist(new Histogram<uint64_t>("UpdateRange", 5));
+ Histogram<uint64_t>::CumulativeData data;
+
+ double PerValue;
+
+ hist->AddValue(15);
+ hist->AddValue(17);
+ hist->AddValue(35);
+ hist->AddValue(50);
+ hist->AddValue(68);
+ hist->AddValue(75);
+ hist->AddValue(93);
+ hist->AddValue(110);
+ hist->AddValue(121);
+ hist->AddValue(132);
+ hist->AddValue(140); // Median value
+ hist->AddValue(145);
+ hist->AddValue(155);
+ hist->AddValue(163);
+ hist->AddValue(168);
+ hist->AddValue(175);
+ hist->AddValue(182);
+ hist->AddValue(193);
+ hist->AddValue(200);
+ hist->AddValue(205);
+ hist->AddValue(212);
+ hist->CreateHistogram(data);
+ PerValue = hist->Percentile(0.50, data);
+
+ std::string text;
+ std::stringstream stream;
+ std::string expected("UpdateRange:\t99% C.I. 15us-212us Avg: 126.380us Max: 212us\n");
+ hist->PrintConfidenceIntervals(stream, 0.99, data);
+
+ EXPECT_EQ(expected, stream.str());
+ EXPECT_GE(PerValue, 132);
+ EXPECT_LE(PerValue, 145);
+}
+
+TEST(Histtest, Reset) {
+ UniquePtr<Histogram<uint64_t> > hist(new Histogram<uint64_t>("Reset", 5));
+ Histogram<uint64_t>::CumulativeData data;
+
+ double PerValue;
+ hist->AddValue(0);
+ hist->AddValue(189);
+ hist->AddValue(389);
+ hist->Reset();
+ hist->AddValue(15);
+ hist->AddValue(17);
+ hist->AddValue(35);
+ hist->AddValue(50);
+ hist->AddValue(68);
+ hist->AddValue(75);
+ hist->AddValue(93);
+ hist->AddValue(110);
+ hist->AddValue(121);
+ hist->AddValue(132);
+ hist->AddValue(140); // Median value
+ hist->AddValue(145);
+ hist->AddValue(155);
+ hist->AddValue(163);
+ hist->AddValue(168);
+ hist->AddValue(175);
+ hist->AddValue(182);
+ hist->AddValue(193);
+ hist->AddValue(200);
+ hist->AddValue(205);
+ hist->AddValue(212);
+ hist->CreateHistogram(data);
+ PerValue = hist->Percentile(0.50, data);
+
+ std::string text;
+ std::stringstream stream;
+ std::string expected("Reset:\t99% C.I. 15us-212us Avg: 126.380us Max: 212us\n");
+ hist->PrintConfidenceIntervals(stream, 0.99, data);
+
+ EXPECT_EQ(expected, stream.str());
+ EXPECT_GE(PerValue, 132);
+ EXPECT_LE(PerValue, 145);
+}
+
+TEST(Histtest, MultipleCreateHist) {
+ UniquePtr<Histogram<uint64_t> > hist(new Histogram<uint64_t>("MultipleCreateHist", 5));
+ Histogram<uint64_t>::CumulativeData data;
+
+ double PerValue;
+ hist->AddValue(15);
+ hist->AddValue(17);
+ hist->AddValue(35);
+ hist->AddValue(50);
+ hist->AddValue(68);
+ hist->AddValue(75);
+ hist->AddValue(93);
+ hist->CreateHistogram(data);
+ hist->AddValue(110);
+ hist->AddValue(121);
+ hist->AddValue(132);
+ hist->AddValue(140); // Median value
+ hist->AddValue(145);
+ hist->AddValue(155);
+ hist->AddValue(163);
+ hist->AddValue(168);
+ hist->CreateHistogram(data);
+ hist->AddValue(175);
+ hist->AddValue(182);
+ hist->AddValue(193);
+ hist->AddValue(200);
+ hist->AddValue(205);
+ hist->AddValue(212);
+ hist->CreateHistogram(data);
+ PerValue = hist->Percentile(0.50, data);
+ std::stringstream stream;
+ std::string expected("MultipleCreateHist:\t99% C.I. 15us-212us Avg: 126.380us Max: 212us\n");
+ hist->PrintConfidenceIntervals(stream, 0.99, data);
+
+ EXPECT_EQ(expected, stream.str());
+ EXPECT_GE(PerValue, 132);
+ EXPECT_LE(PerValue, 145);
+}
+
+TEST(Histtest, SingleValue) {
+ UniquePtr<Histogram<uint64_t> > hist(new Histogram<uint64_t>("SingleValue", 5));
+ Histogram<uint64_t>::CumulativeData data;
+
+ hist->AddValue(1);
+ hist->CreateHistogram(data);
+ std::stringstream stream;
+ std::string expected = "SingleValue:\t99% C.I. 1us-1us Avg: 1us Max: 1us\n";
+ hist->PrintConfidenceIntervals(stream, 0.99, data);
+ EXPECT_EQ(expected, stream.str());
+}
+
+TEST(Histtest, CappingPercentiles) {
+ UniquePtr<Histogram<uint64_t> > hist(new Histogram<uint64_t>("CappingPercentiles", 5));
+ Histogram<uint64_t>::CumulativeData data;
+
+ double per_995;
+ double per_005;
+ // All values are similar.
+ for (uint64_t idx = 0ull; idx < 150ull; idx++) {
+ hist->AddValue(0);
+ }
+ hist->CreateHistogram(data);
+ per_995 = hist->Percentile(0.995, data);
+ EXPECT_EQ(per_995, 0);
+ hist->Reset();
+ for (size_t idx = 0; idx < 200; idx++) {
+ for (uint64_t val = 1ull; val <= 4ull; val++) {
+ hist->AddValue(val);
+ }
+ }
+ hist->CreateHistogram(data);
+ per_005 = hist->Percentile(0.005, data);
+ per_995 = hist->Percentile(0.995, data);
+ EXPECT_EQ(1, per_005);
+ EXPECT_EQ(4, per_995);
+}
+
+TEST(Histtest, SpikyValues) {
+ UniquePtr<Histogram<uint64_t> > hist(new Histogram<uint64_t>("SpikyValues", 5, 4096));
+ Histogram<uint64_t>::CumulativeData data;
+
+ for (uint64_t idx = 0ull; idx < 30ull; idx++) {
+ for (uint64_t idx_inner = 0ull; idx_inner < 5ull; idx_inner++) {
+ hist->AddValue(idx * idx_inner);
+ }
+ }
+ hist->AddValue(10000);
+ hist->CreateHistogram(data);
+ std::stringstream stream;
+ std::string expected = "SpikyValues:\t99% C.I. 0.089us-2541.825us Avg: 95.033us Max: 10000us\n";
+ hist->PrintConfidenceIntervals(stream, 0.99, data);
+ EXPECT_EQ(expected, stream.str());
+}
+
+} // namespace art
diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc
new file mode 100644
index 0000000..7d54baf
--- /dev/null
+++ b/runtime/base/logging.cc
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "logging.h"
+
+#include "base/mutex.h"
+#include "runtime.h"
+#include "thread.h"
+#include "utils.h"
+
+namespace art {
+
+LogVerbosity gLogVerbosity;
+
+unsigned int gAborting = 0;
+
+static LogSeverity gMinimumLogSeverity = INFO;
+static std::string* gCmdLine = NULL;
+static std::string* gProgramInvocationName = NULL;
+static std::string* gProgramInvocationShortName = NULL;
+
+const char* GetCmdLine() {
+ return (gCmdLine != NULL) ? gCmdLine->c_str() : NULL;
+}
+
+const char* ProgramInvocationName() {
+ return (gProgramInvocationName != NULL) ? gProgramInvocationName->c_str() : "art";
+}
+
+const char* ProgramInvocationShortName() {
+ return (gProgramInvocationShortName != NULL) ? gProgramInvocationShortName->c_str() : "art";
+}
+
+// Configure logging based on ANDROID_LOG_TAGS environment variable.
+// We need to parse a string that looks like
+//
+// *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
+//
+// The tag (or '*' for the global level) comes first, followed by a colon
+// and a letter indicating the minimum priority level we're expected to log.
+// This can be used to reveal or conceal logs with specific tags.
+void InitLogging(char* argv[]) {
+ if (gCmdLine != NULL) {
+ return;
+ }
+ // TODO: Move this to a more obvious InitART...
+ Locks::Init();
+
+ // Stash the command line for later use. We can use /proc/self/cmdline on Linux to recover this,
+ // but we don't have that luxury on the Mac, and there are a couple of argv[0] variants that are
+ // commonly used.
+ if (argv != NULL) {
+ gCmdLine = new std::string(argv[0]);
+ for (size_t i = 1; argv[i] != NULL; ++i) {
+ gCmdLine->append(" ");
+ gCmdLine->append(argv[i]);
+ }
+ gProgramInvocationName = new std::string(argv[0]);
+ const char* last_slash = strrchr(argv[0], '/');
+ gProgramInvocationShortName = new std::string((last_slash != NULL) ? last_slash + 1 : argv[0]);
+ } else {
+ // TODO: fall back to /proc/self/cmdline when argv is NULL on Linux
+ gCmdLine = new std::string("<unset>");
+ }
+ const char* tags = getenv("ANDROID_LOG_TAGS");
+ if (tags == NULL) {
+ return;
+ }
+
+ std::vector<std::string> specs;
+ Split(tags, ' ', specs);
+ for (size_t i = 0; i < specs.size(); ++i) {
+ // "tag-pattern:[vdiwefs]"
+ std::string spec(specs[i]);
+ if (spec.size() == 3 && StartsWith(spec, "*:")) {
+ switch (spec[2]) {
+ case 'v':
+ gMinimumLogSeverity = VERBOSE;
+ continue;
+ case 'd':
+ gMinimumLogSeverity = DEBUG;
+ continue;
+ case 'i':
+ gMinimumLogSeverity = INFO;
+ continue;
+ case 'w':
+ gMinimumLogSeverity = WARNING;
+ continue;
+ case 'e':
+ gMinimumLogSeverity = ERROR;
+ continue;
+ case 'f':
+ gMinimumLogSeverity = FATAL;
+ continue;
+ // liblog will even suppress FATAL if you say 's' for silent, but that's crazy!
+ case 's':
+ gMinimumLogSeverity = FATAL;
+ continue;
+ }
+ }
+ LOG(FATAL) << "unsupported '" << spec << "' in ANDROID_LOG_TAGS (" << tags << ")";
+ }
+}
+
+LogMessageData::LogMessageData(const char* file, int line, LogSeverity severity, int error)
+ : file(file),
+ line_number(line),
+ severity(severity),
+ error(error) {
+ const char* last_slash = strrchr(file, '/');
+ file = (last_slash == NULL) ? file : last_slash + 1;
+}
+
+LogMessage::~LogMessage() {
+ if (data_->severity < gMinimumLogSeverity) {
+ return; // No need to format something we're not going to output.
+ }
+
+ // Finish constructing the message.
+ if (data_->error != -1) {
+ data_->buffer << ": " << strerror(data_->error);
+ }
+ std::string msg(data_->buffer.str());
+
+ // Do the actual logging with the lock held.
+ {
+ MutexLock mu(Thread::Current(), *Locks::logging_lock_);
+ if (msg.find('\n') == std::string::npos) {
+ LogLine(*data_, msg.c_str());
+ } else {
+ msg += '\n';
+ size_t i = 0;
+ while (i < msg.size()) {
+ size_t nl = msg.find('\n', i);
+ msg[nl] = '\0';
+ LogLine(*data_, &msg[i]);
+ i = nl + 1;
+ }
+ }
+ }
+
+ // Abort if necessary.
+ if (data_->severity == FATAL) {
+ Runtime::Abort();
+ }
+}
+
+HexDump::HexDump(const void* address, size_t byte_count, bool show_actual_addresses)
+ : address_(address), byte_count_(byte_count), show_actual_addresses_(show_actual_addresses) {
+}
+
+void HexDump::Dump(std::ostream& os) const {
+ if (byte_count_ == 0) {
+ return;
+ }
+
+ if (address_ == NULL) {
+ os << "00000000:";
+ return;
+ }
+
+ static const char gHexDigit[] = "0123456789abcdef";
+ const unsigned char* addr = reinterpret_cast<const unsigned char*>(address_);
+ char out[76]; /* exact fit */
+ unsigned int offset; /* offset to show while printing */
+
+ if (show_actual_addresses_) {
+ offset = reinterpret_cast<int>(addr);
+ } else {
+ offset = 0;
+ }
+ memset(out, ' ', sizeof(out)-1);
+ out[8] = ':';
+ out[sizeof(out)-1] = '\0';
+
+ size_t byte_count = byte_count_;
+ int gap = static_cast<int>(offset & 0x0f);
+ while (byte_count) {
+ unsigned int line_offset = offset & ~0x0f;
+
+ char* hex = out;
+ char* asc = out + 59;
+
+ for (int i = 0; i < 8; i++) {
+ *hex++ = gHexDigit[line_offset >> 28];
+ line_offset <<= 4;
+ }
+ hex++;
+ hex++;
+
+ int count = std::min(static_cast<int>(byte_count), 16 - gap);
+ CHECK_NE(count, 0);
+ CHECK_LE(count + gap, 16);
+
+ if (gap) {
+ /* only on first line */
+ hex += gap * 3;
+ asc += gap;
+ }
+
+ int i;
+ for (i = gap ; i < count+gap; i++) {
+ *hex++ = gHexDigit[*addr >> 4];
+ *hex++ = gHexDigit[*addr & 0x0f];
+ hex++;
+ if (*addr >= 0x20 && *addr < 0x7f /*isprint(*addr)*/) {
+ *asc++ = *addr;
+ } else {
+ *asc++ = '.';
+ }
+ addr++;
+ }
+ for (; i < 16; i++) {
+ /* erase extra stuff; only happens on last line */
+ *hex++ = ' ';
+ *hex++ = ' ';
+ hex++;
+ *asc++ = ' ';
+ }
+
+ os << out;
+
+ gap = 0;
+ byte_count -= count;
+ offset += count;
+ }
+}
+
+std::ostream& operator<<(std::ostream& os, const HexDump& rhs) {
+ rhs.Dump(os);
+ return os;
+}
+
+} // namespace art
diff --git a/runtime/base/logging.h b/runtime/base/logging.h
new file mode 100644
index 0000000..8e40da0
--- /dev/null
+++ b/runtime/base/logging.h
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_BASE_LOGGING_H_
+#define ART_RUNTIME_BASE_LOGGING_H_
+
+#include <cerrno>
+#include <cstring>
+#include <iostream> // NOLINT
+#include <sstream>
+#include <signal.h>
+#include "base/macros.h"
+#include "log_severity.h"
+#include "UniquePtr.h"
+
+#define CHECK(x) \
+ if (UNLIKELY(!(x))) \
+ ::art::LogMessage(__FILE__, __LINE__, FATAL, -1).stream() \
+ << "Check failed: " #x << " "
+
+#define CHECK_OP(LHS, RHS, OP) \
+ for (auto _values = ::art::MakeEagerEvaluator(LHS, RHS); \
+ UNLIKELY(!(_values.lhs OP _values.rhs)); /* empty */) \
+ ::art::LogMessage(__FILE__, __LINE__, FATAL, -1).stream() \
+ << "Check failed: " << #LHS << " " << #OP << " " << #RHS \
+ << " (" #LHS "=" << _values.lhs << ", " #RHS "=" << _values.rhs << ") "
+
+#define CHECK_EQ(x, y) CHECK_OP(x, y, ==)
+#define CHECK_NE(x, y) CHECK_OP(x, y, !=)
+#define CHECK_LE(x, y) CHECK_OP(x, y, <=)
+#define CHECK_LT(x, y) CHECK_OP(x, y, <)
+#define CHECK_GE(x, y) CHECK_OP(x, y, >=)
+#define CHECK_GT(x, y) CHECK_OP(x, y, >)
+
+#define CHECK_STROP(s1, s2, sense) \
+ if (UNLIKELY((strcmp(s1, s2) == 0) != sense)) \
+ LOG(FATAL) << "Check failed: " \
+ << "\"" << s1 << "\"" \
+ << (sense ? " == " : " != ") \
+ << "\"" << s2 << "\""
+
+#define CHECK_STREQ(s1, s2) CHECK_STROP(s1, s2, true)
+#define CHECK_STRNE(s1, s2) CHECK_STROP(s1, s2, false)
+
+#define CHECK_PTHREAD_CALL(call, args, what) \
+ do { \
+ int rc = call args; \
+ if (rc != 0) { \
+ errno = rc; \
+ PLOG(FATAL) << # call << " failed for " << what; \
+ } \
+ } while (false)
+
+#ifndef NDEBUG
+
+#define DCHECK(x) CHECK(x)
+#define DCHECK_EQ(x, y) CHECK_EQ(x, y)
+#define DCHECK_NE(x, y) CHECK_NE(x, y)
+#define DCHECK_LE(x, y) CHECK_LE(x, y)
+#define DCHECK_LT(x, y) CHECK_LT(x, y)
+#define DCHECK_GE(x, y) CHECK_GE(x, y)
+#define DCHECK_GT(x, y) CHECK_GT(x, y)
+#define DCHECK_STREQ(s1, s2) CHECK_STREQ(s1, s2)
+#define DCHECK_STRNE(s1, s2) CHECK_STRNE(s1, s2)
+
+#else // NDEBUG
+
+#define DCHECK(condition) \
+ while (false) \
+ CHECK(condition)
+
+#define DCHECK_EQ(val1, val2) \
+ while (false) \
+ CHECK_EQ(val1, val2)
+
+#define DCHECK_NE(val1, val2) \
+ while (false) \
+ CHECK_NE(val1, val2)
+
+#define DCHECK_LE(val1, val2) \
+ while (false) \
+ CHECK_LE(val1, val2)
+
+#define DCHECK_LT(val1, val2) \
+ while (false) \
+ CHECK_LT(val1, val2)
+
+#define DCHECK_GE(val1, val2) \
+ while (false) \
+ CHECK_GE(val1, val2)
+
+#define DCHECK_GT(val1, val2) \
+ while (false) \
+ CHECK_GT(val1, val2)
+
+#define DCHECK_STREQ(str1, str2) \
+ while (false) \
+ CHECK_STREQ(str1, str2)
+
+#define DCHECK_STRNE(str1, str2) \
+ while (false) \
+ CHECK_STRNE(str1, str2)
+
+#endif
+
+#define LOG(severity) ::art::LogMessage(__FILE__, __LINE__, severity, -1).stream()
+#define PLOG(severity) ::art::LogMessage(__FILE__, __LINE__, severity, errno).stream()
+
+#define LG LOG(INFO)
+
+#define UNIMPLEMENTED(level) LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
+
+#define VLOG_IS_ON(module) UNLIKELY(::art::gLogVerbosity.module)
+#define VLOG(module) if (VLOG_IS_ON(module)) ::art::LogMessage(__FILE__, __LINE__, INFO, -1).stream()
+#define VLOG_STREAM(module) ::art::LogMessage(__FILE__, __LINE__, INFO, -1).stream()
+
+//
+// Implementation details beyond this point.
+//
+
+namespace art {
+
+template <typename LHS, typename RHS>
+struct EagerEvaluator {
+ EagerEvaluator(LHS lhs, RHS rhs) : lhs(lhs), rhs(rhs) { }
+ LHS lhs;
+ RHS rhs;
+};
+
+// We want char*s to be treated as pointers, not strings. If you want them treated like strings,
+// you'd need to use CHECK_STREQ and CHECK_STRNE anyway to compare the characters rather than their
+// addresses. We could express this more succinctly with std::remove_const, but this is quick and
+// easy to understand, and works before we have C++0x. We rely on signed/unsigned warnings to
+// protect you against combinations not explicitly listed below.
+#define EAGER_PTR_EVALUATOR(T1, T2) \
+ template <> struct EagerEvaluator<T1, T2> { \
+ EagerEvaluator(T1 lhs, T2 rhs) \
+ : lhs(reinterpret_cast<const void*>(lhs)), \
+ rhs(reinterpret_cast<const void*>(rhs)) { } \
+ const void* lhs; \
+ const void* rhs; \
+ }
+EAGER_PTR_EVALUATOR(const char*, const char*);
+EAGER_PTR_EVALUATOR(const char*, char*);
+EAGER_PTR_EVALUATOR(char*, const char*);
+EAGER_PTR_EVALUATOR(char*, char*);
+EAGER_PTR_EVALUATOR(const unsigned char*, const unsigned char*);
+EAGER_PTR_EVALUATOR(const unsigned char*, unsigned char*);
+EAGER_PTR_EVALUATOR(unsigned char*, const unsigned char*);
+EAGER_PTR_EVALUATOR(unsigned char*, unsigned char*);
+EAGER_PTR_EVALUATOR(const signed char*, const signed char*);
+EAGER_PTR_EVALUATOR(const signed char*, signed char*);
+EAGER_PTR_EVALUATOR(signed char*, const signed char*);
+EAGER_PTR_EVALUATOR(signed char*, signed char*);
+
+template <typename LHS, typename RHS>
+EagerEvaluator<LHS, RHS> MakeEagerEvaluator(LHS lhs, RHS rhs) {
+ return EagerEvaluator<LHS, RHS>(lhs, rhs);
+}
+
+// This indirection greatly reduces the stack impact of having
+// lots of checks/logging in a function.
+struct LogMessageData {
+ public:
+ LogMessageData(const char* file, int line, LogSeverity severity, int error);
+ std::ostringstream buffer;
+ const char* const file;
+ const int line_number;
+ const LogSeverity severity;
+ const int error;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LogMessageData);
+};
+
+class LogMessage {
+ public:
+ LogMessage(const char* file, int line, LogSeverity severity, int error)
+ : data_(new LogMessageData(file, line, severity, error)) {
+ }
+
+ ~LogMessage() LOCKS_EXCLUDED(Locks::logging_lock_);
+
+ std::ostream& stream() {
+ return data_->buffer;
+ }
+
+ private:
+ static void LogLine(const LogMessageData& data, const char*);
+
+ const UniquePtr<LogMessageData> data_;
+
+ friend void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_context);
+ friend class Mutex;
+ DISALLOW_COPY_AND_ASSIGN(LogMessage);
+};
+
+// Prints a hex dump in this format:
+//
+// 01234560: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff 0123456789abcdef
+// 01234568: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff 0123456789abcdef
+class HexDump {
+ public:
+ HexDump(const void* address, size_t byte_count, bool show_actual_addresses = false);
+ void Dump(std::ostream& os) const;
+
+ private:
+ const void* address_;
+ size_t byte_count_;
+ bool show_actual_addresses_;
+
+ DISALLOW_COPY_AND_ASSIGN(HexDump);
+};
+std::ostream& operator<<(std::ostream& os, const HexDump& rhs);
+
+// A convenience to allow any class with a "Dump(std::ostream& os)" member function
+// but without an operator<< to be used as if it had an operator<<. Use like this:
+//
+// os << Dumpable<MyType>(my_type_instance);
+//
+template<typename T>
+class Dumpable {
+ public:
+ explicit Dumpable(T& value) : value_(value) {
+ }
+
+ void Dump(std::ostream& os) const {
+ value_.Dump(os);
+ }
+
+ private:
+ T& value_;
+
+ DISALLOW_COPY_AND_ASSIGN(Dumpable);
+};
+
+template<typename T>
+std::ostream& operator<<(std::ostream& os, const Dumpable<T>& rhs) {
+ rhs.Dump(os);
+ return os;
+}
+
+template<typename T>
+class MutatorLockedDumpable {
+ public:
+ explicit MutatorLockedDumpable(T& value)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : value_(value) {
+ }
+
+ void Dump(std::ostream& os) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ value_.Dump(os);
+ }
+
+ private:
+ T& value_;
+
+ DISALLOW_COPY_AND_ASSIGN(MutatorLockedDumpable);
+};
+
+template<typename T>
+std::ostream& operator<<(std::ostream& os, const MutatorLockedDumpable<T>& rhs)
+// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) however annotalysis
+// currently fails for this.
+ NO_THREAD_SAFETY_ANALYSIS {
+ rhs.Dump(os);
+ return os;
+}
+
+// Helps you use operator<< in a const char*-like context such as our various 'F' methods with
+// format strings.
+template<typename T>
+class ToStr {
+ public:
+ explicit ToStr(const T& value) {
+ std::ostringstream os;
+ os << value;
+ s_ = os.str();
+ }
+
+ const char* c_str() const {
+ return s_.c_str();
+ }
+
+ const std::string& str() const {
+ return s_;
+ }
+
+ private:
+ std::string s_;
+ DISALLOW_COPY_AND_ASSIGN(ToStr);
+};
+
+// The members of this struct are the valid arguments to VLOG and VLOG_IS_ON in code,
+// and the "-verbose:" command line argument.
+struct LogVerbosity {
+ bool class_linker; // Enabled with "-verbose:class".
+ bool verifier;
+ bool compiler;
+ bool heap;
+ bool gc;
+ bool jdwp;
+ bool jni;
+ bool monitor;
+ bool startup;
+ bool third_party_jni; // Enabled with "-verbose:third-party-jni".
+ bool threads;
+};
+
+extern LogVerbosity gLogVerbosity;
+
+// Used on fatal exit. Prevents recursive aborts. Allows us to disable
+// some error checking to ensure fatal shutdown makes forward progress.
+extern unsigned int gAborting;
+
+extern void InitLogging(char* argv[]);
+
+extern const char* GetCmdLine();
+extern const char* ProgramInvocationName();
+extern const char* ProgramInvocationShortName();
+
+} // namespace art
+
+#endif // ART_RUNTIME_BASE_LOGGING_H_
diff --git a/runtime/base/logging_android.cc b/runtime/base/logging_android.cc
new file mode 100644
index 0000000..9b1ac58
--- /dev/null
+++ b/runtime/base/logging_android.cc
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "logging.h"
+
+#include <unistd.h>
+
+#include <iostream>
+
+#include "base/stringprintf.h"
+#include "cutils/log.h"
+
+namespace art {
+
+static const int kLogSeverityToAndroidLogPriority[] = {
+ ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO, ANDROID_LOG_WARN,
+ ANDROID_LOG_ERROR, ANDROID_LOG_FATAL, ANDROID_LOG_FATAL
+};
+
+void LogMessage::LogLine(const LogMessageData& data, const char* message) {
+ const char* tag = ProgramInvocationShortName();
+ int priority = kLogSeverityToAndroidLogPriority[data.severity];
+ if (priority == ANDROID_LOG_FATAL) {
+ LOG_PRI(priority, tag, "%s:%d] %s", data.file, data.line_number, message);
+ } else {
+ LOG_PRI(priority, tag, "%s", message);
+ }
+}
+
+} // namespace art
diff --git a/runtime/base/logging_linux.cc b/runtime/base/logging_linux.cc
new file mode 100644
index 0000000..0399128
--- /dev/null
+++ b/runtime/base/logging_linux.cc
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "logging.h"
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+
+#include "base/stringprintf.h"
+#include "utils.h"
+
+namespace art {
+
+void LogMessage::LogLine(const LogMessageData& data, const char* message) {
+ char severity = "VDIWEFF"[data.severity];
+ fprintf(stderr, "%s %c %5d %5d %s:%d] %s\n",
+ ProgramInvocationShortName(), severity, getpid(), ::art::GetTid(),
+ data.file, data.line_number, message);
+}
+
+} // namespace art
diff --git a/runtime/base/macros.h b/runtime/base/macros.h
new file mode 100644
index 0000000..6531858
--- /dev/null
+++ b/runtime/base/macros.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_BASE_MACROS_H_
+#define ART_RUNTIME_BASE_MACROS_H_
+
+#include <stddef.h> // for size_t
+
+#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
+
+// The COMPILE_ASSERT macro can be used to verify that a compile time
+// expression is true. For example, you could use it to verify the
+// size of a static array:
+//
+// COMPILE_ASSERT(ARRAYSIZE(content_type_names) == CONTENT_NUM_TYPES,
+// content_type_names_incorrect_size);
+//
+// or to make sure a struct is smaller than a certain size:
+//
+// COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large);
+//
+// The second argument to the macro is the name of the variable. If
+// the expression is false, most compilers will issue a warning/error
+// containing the name of the variable.
+
+template <bool>
+struct CompileAssert {
+};
+
+#define COMPILE_ASSERT(expr, msg) \
+ typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] // NOLINT
+
+// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions.
+// It goes in the private: declarations in a class.
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
+
+// A macro to disallow all the implicit constructors, namely the
+// default constructor, copy constructor and operator= functions.
+//
+// This should be used in the private: declarations for a class
+// that wants to prevent anyone from instantiating it. This is
+// especially useful for classes containing only static methods.
+#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+ TypeName(); \
+ DISALLOW_COPY_AND_ASSIGN(TypeName)
+
+// The arraysize(arr) macro returns the # of elements in an array arr.
+// The expression is a compile-time constant, and therefore can be
+// used in defining new arrays, for example. If you use arraysize on
+// a pointer by mistake, you will get a compile-time error.
+//
+// One caveat is that arraysize() doesn't accept any array of an
+// anonymous type or a type defined inside a function. In these rare
+// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below. This is
+// due to a limitation in C++'s template system. The limitation might
+// eventually be removed, but it hasn't happened yet.
+
+// This template function declaration is used in defining arraysize.
+// Note that the function doesn't need an implementation, as we only
+// use its type.
+template <typename T, size_t N>
+char (&ArraySizeHelper(T (&array)[N]))[N];
+
+#define arraysize(array) (sizeof(ArraySizeHelper(array)))
+
+// ARRAYSIZE_UNSAFE performs essentially the same calculation as arraysize,
+// but can be used on anonymous types or types defined inside
+// functions. It's less safe than arraysize as it accepts some
+// (although not all) pointers. Therefore, you should use arraysize
+// whenever possible.
+//
+// The expression ARRAYSIZE_UNSAFE(a) is a compile-time constant of type
+// size_t.
+//
+// ARRAYSIZE_UNSAFE catches a few type errors. If you see a compiler error
+//
+// "warning: division by zero in ..."
+//
+// when using ARRAYSIZE_UNSAFE, you are (wrongfully) giving it a pointer.
+// You should only use ARRAYSIZE_UNSAFE on statically allocated arrays.
+//
+// The following comments are on the implementation details, and can
+// be ignored by the users.
+//
+// ARRAYSIZE_UNSAFE(arr) works by inspecting sizeof(arr) (the # of bytes in
+// the array) and sizeof(*(arr)) (the # of bytes in one array
+// element). If the former is divisible by the latter, perhaps arr is
+// indeed an array, in which case the division result is the # of
+// elements in the array. Otherwise, arr cannot possibly be an array,
+// and we generate a compiler error to prevent the code from
+// compiling.
+//
+// Since the size of bool is implementation-defined, we need to cast
+// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final
+// result has type size_t.
+//
+// This macro is not perfect as it wrongfully accepts certain
+// pointers, namely where the pointer size is divisible by the pointee
+// size. Since all our code has to go through a 32-bit compiler,
+// where a pointer is 4 bytes, this means all pointers to a type whose
+// size is 3 or greater than 4 will be (righteously) rejected.
+#define ARRAYSIZE_UNSAFE(a) \
+ ((sizeof(a) / sizeof(*(a))) / static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
+
+#define SIZEOF_MEMBER(t, f) sizeof((reinterpret_cast<t*>(4096))->f)
+
+#define OFFSETOF_MEMBER(t, f) \
+ (reinterpret_cast<const char*>(&reinterpret_cast<t*>(16)->f) - reinterpret_cast<const char*>(16)) // NOLINT
+
+#define OFFSETOF_VOLATILE_MEMBER(t, f) \
+ (reinterpret_cast<volatile char*>(&reinterpret_cast<t*>(16)->f) - reinterpret_cast<volatile char*>(16)) // NOLINT
+
+#define PACKED(x) __attribute__ ((__aligned__(x), __packed__))
+
+#define LIKELY(x) __builtin_expect((x), true)
+#define UNLIKELY(x) __builtin_expect((x), false)
+
+#ifndef NDEBUG
+#define ALWAYS_INLINE
+#else
+#define ALWAYS_INLINE __attribute__ ((always_inline))
+#endif
+
+#if defined (__APPLE__)
+#define HOT_ATTR
+#else
+#define HOT_ATTR __attribute__ ((hot))
+#endif
+
+#define PURE __attribute__ ((__pure__))
+
+// bionic and glibc both have TEMP_FAILURE_RETRY, but Mac OS' libc doesn't.
+#ifndef TEMP_FAILURE_RETRY
+#define TEMP_FAILURE_RETRY(exp) ({ \
+ typeof(exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1 && errno == EINTR); \
+ _rc; })
+#endif
+
+template<typename T> void UNUSED(const T&) {}
+
+#if defined(__SUPPORT_TS_ANNOTATION__)
+
+#define ACQUIRED_AFTER(...) __attribute__ ((acquired_after(__VA_ARGS__)))
+#define ACQUIRED_BEFORE(...) __attribute__ ((acquired_before(__VA_ARGS__)))
+#define EXCLUSIVE_LOCK_FUNCTION(...) __attribute__ ((exclusive_lock(__VA_ARGS__)))
+#define EXCLUSIVE_LOCKS_REQUIRED(...) __attribute__ ((exclusive_locks_required(__VA_ARGS__)))
+#define EXCLUSIVE_TRYLOCK_FUNCTION(...) __attribute__ ((exclusive_trylock(__VA_ARGS__)))
+#define GUARDED_BY(x) __attribute__ ((guarded_by(x)))
+#define GUARDED_VAR __attribute__ ((guarded))
+#define LOCKABLE __attribute__ ((lockable))
+#define LOCK_RETURNED(x) __attribute__ ((lock_returned(x)))
+#define LOCKS_EXCLUDED(...) __attribute__ ((locks_excluded(__VA_ARGS__)))
+#define NO_THREAD_SAFETY_ANALYSIS __attribute__ ((no_thread_safety_analysis))
+#define PT_GUARDED_BY(x) __attribute__ ((point_to_guarded_by(x)))
+#define PT_GUARDED_VAR __attribute__ ((point_to_guarded))
+#define SCOPED_LOCKABLE __attribute__ ((scoped_lockable))
+#define SHARED_LOCK_FUNCTION(...) __attribute__ ((shared_lock(__VA_ARGS__)))
+#define SHARED_LOCKS_REQUIRED(...) __attribute__ ((shared_locks_required(__VA_ARGS__)))
+#define SHARED_TRYLOCK_FUNCTION(...) __attribute__ ((shared_trylock(__VA_ARGS__)))
+#define UNLOCK_FUNCTION(...) __attribute__ ((unlock(__VA_ARGS__)))
+
+#else
+
+#define ACQUIRED_AFTER(...)
+#define ACQUIRED_BEFORE(...)
+#define EXCLUSIVE_LOCK_FUNCTION(...)
+#define EXCLUSIVE_LOCKS_REQUIRED(...)
+#define EXCLUSIVE_TRYLOCK_FUNCTION(...)
+#define GUARDED_BY(x)
+#define GUARDED_VAR
+#define LOCKABLE
+#define LOCK_RETURNED(x)
+#define LOCKS_EXCLUDED(...)
+#define NO_THREAD_SAFETY_ANALYSIS
+#define PT_GUARDED_BY(x)
+#define PT_GUARDED_VAR
+#define SCOPED_LOCKABLE
+#define SHARED_LOCK_FUNCTION(...)
+#define SHARED_LOCKS_REQUIRED(...)
+#define SHARED_TRYLOCK_FUNCTION(...)
+#define UNLOCK_FUNCTION(...)
+
+#endif // defined(__SUPPORT_TS_ANNOTATION__)
+
+#endif // ART_RUNTIME_BASE_MACROS_H_
diff --git a/runtime/base/mutex-inl.h b/runtime/base/mutex-inl.h
new file mode 100644
index 0000000..7e8365e
--- /dev/null
+++ b/runtime/base/mutex-inl.h
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_BASE_MUTEX_INL_H_
+#define ART_RUNTIME_BASE_MUTEX_INL_H_
+
+#include "mutex.h"
+
+#define ATRACE_TAG ATRACE_TAG_DALVIK
+
+#include "cutils/atomic-inline.h"
+#include "cutils/trace.h"
+#include "runtime.h"
+#include "thread.h"
+
+namespace art {
+
+#define CHECK_MUTEX_CALL(call, args) CHECK_PTHREAD_CALL(call, args, name_)
+
+#if ART_USE_FUTEXES
+#include "linux/futex.h"
+#include "sys/syscall.h"
+#ifndef SYS_futex
+#define SYS_futex __NR_futex
+#endif
+static inline int futex(volatile int *uaddr, int op, int val, const struct timespec *timeout, volatile int *uaddr2, int val3) {
+ return syscall(SYS_futex, uaddr, op, val, timeout, uaddr2, val3);
+}
+#endif // ART_USE_FUTEXES
+
+class ScopedContentionRecorder {
+ public:
+ ScopedContentionRecorder(BaseMutex* mutex, uint64_t blocked_tid, uint64_t owner_tid)
+ : mutex_(kLogLockContentions ? mutex : NULL),
+ blocked_tid_(kLogLockContentions ? blocked_tid : 0),
+ owner_tid_(kLogLockContentions ? owner_tid : 0),
+ start_nano_time_(kLogLockContentions ? NanoTime() : 0) {
+ std::string msg = StringPrintf("Lock contention on %s (owner tid: %llu)",
+ mutex->GetName(), owner_tid);
+ ATRACE_BEGIN(msg.c_str());
+ }
+
+ ~ScopedContentionRecorder() {
+ ATRACE_END();
+ if (kLogLockContentions) {
+ uint64_t end_nano_time = NanoTime();
+ mutex_->RecordContention(blocked_tid_, owner_tid_, end_nano_time - start_nano_time_);
+ }
+ }
+
+ private:
+ BaseMutex* const mutex_;
+ const uint64_t blocked_tid_;
+ const uint64_t owner_tid_;
+ const uint64_t start_nano_time_;
+};
+
+static inline uint64_t SafeGetTid(const Thread* self) {
+ if (self != NULL) {
+ return static_cast<uint64_t>(self->GetTid());
+ } else {
+ return static_cast<uint64_t>(GetTid());
+ }
+}
+
+static inline void CheckUnattachedThread(LockLevel level) NO_THREAD_SAFETY_ANALYSIS {
+ // The check below enumerates the cases where we expect not to be able to sanity check locks
+ // on a thread. Lock checking is disabled to avoid deadlock when checking shutdown lock.
+ // TODO: tighten this check.
+ if (kDebugLocking) {
+ Runtime* runtime = Runtime::Current();
+ CHECK(runtime == NULL || !runtime->IsStarted() || runtime->IsShuttingDown() ||
+ level == kDefaultMutexLevel || level == kRuntimeShutdownLock ||
+ level == kThreadListLock || level == kLoggingLock || level == kAbortLock);
+ }
+}
+
+inline void BaseMutex::RegisterAsLocked(Thread* self) {
+ if (UNLIKELY(self == NULL)) {
+ CheckUnattachedThread(level_);
+ return;
+ }
+ if (kDebugLocking) {
+ // Check if a bad Mutex of this level or lower is held.
+ bool bad_mutexes_held = false;
+ for (int i = level_; i >= 0; --i) {
+ BaseMutex* held_mutex = self->GetHeldMutex(static_cast<LockLevel>(i));
+ if (UNLIKELY(held_mutex != NULL)) {
+ LOG(ERROR) << "Lock level violation: holding \"" << held_mutex->name_ << "\" "
+ << "(level " << LockLevel(i) << " - " << i
+ << ") while locking \"" << name_ << "\" "
+ << "(level " << level_ << " - " << static_cast<int>(level_) << ")";
+ if (i > kAbortLock) {
+ // Only abort in the check below if this is more than abort level lock.
+ bad_mutexes_held = true;
+ }
+ }
+ }
+ CHECK(!bad_mutexes_held);
+ }
+ // Don't record monitors as they are outside the scope of analysis. They may be inspected off of
+ // the monitor list.
+ if (level_ != kMonitorLock) {
+ self->SetHeldMutex(level_, this);
+ }
+}
+
+inline void BaseMutex::RegisterAsUnlocked(Thread* self) {
+ if (UNLIKELY(self == NULL)) {
+ CheckUnattachedThread(level_);
+ return;
+ }
+ if (level_ != kMonitorLock) {
+ if (kDebugLocking && !gAborting) {
+ CHECK(self->GetHeldMutex(level_) == this) << "Unlocking on unacquired mutex: " << name_;
+ }
+ self->SetHeldMutex(level_, NULL);
+ }
+}
+
+inline void ReaderWriterMutex::SharedLock(Thread* self) {
+ DCHECK(self == NULL || self == Thread::Current());
+#if ART_USE_FUTEXES
+ bool done = false;
+ do {
+ int32_t cur_state = state_;
+ if (LIKELY(cur_state >= 0)) {
+ // Add as an extra reader.
+ done = android_atomic_acquire_cas(cur_state, cur_state + 1, &state_) == 0;
+ } else {
+ // Owner holds it exclusively, hang up.
+ ScopedContentionRecorder scr(this, GetExclusiveOwnerTid(), SafeGetTid(self));
+ android_atomic_inc(&num_pending_readers_);
+ if (futex(&state_, FUTEX_WAIT, cur_state, NULL, NULL, 0) != 0) {
+ if (errno != EAGAIN) {
+ PLOG(FATAL) << "futex wait failed for " << name_;
+ }
+ }
+ android_atomic_dec(&num_pending_readers_);
+ }
+ } while (!done);
+#else
+ CHECK_MUTEX_CALL(pthread_rwlock_rdlock, (&rwlock_));
+#endif
+ RegisterAsLocked(self);
+ AssertSharedHeld(self);
+}
+
+inline void ReaderWriterMutex::SharedUnlock(Thread* self) {
+ DCHECK(self == NULL || self == Thread::Current());
+ AssertSharedHeld(self);
+ RegisterAsUnlocked(self);
+#if ART_USE_FUTEXES
+ bool done = false;
+ do {
+ int32_t cur_state = state_;
+ if (LIKELY(cur_state > 0)) {
+ // Reduce state by 1.
+ done = android_atomic_release_cas(cur_state, cur_state - 1, &state_) == 0;
+ if (done && (cur_state - 1) == 0) { // cas may fail due to noise?
+ if (num_pending_writers_ > 0 || num_pending_readers_ > 0) {
+ // Wake any exclusive waiters as there are now no readers.
+ futex(&state_, FUTEX_WAKE, -1, NULL, NULL, 0);
+ }
+ }
+ } else {
+ LOG(FATAL) << "Unexpected state_:" << cur_state << " for " << name_;
+ }
+ } while (!done);
+#else
+ CHECK_MUTEX_CALL(pthread_rwlock_unlock, (&rwlock_));
+#endif
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_BASE_MUTEX_INL_H_
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
new file mode 100644
index 0000000..b99e7c9
--- /dev/null
+++ b/runtime/base/mutex.cc
@@ -0,0 +1,920 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mutex.h"
+
+#include <errno.h>
+#include <sys/time.h>
+
+#include "atomic.h"
+#include "base/logging.h"
+#include "cutils/atomic.h"
+#include "cutils/atomic-inline.h"
+#include "mutex-inl.h"
+#include "runtime.h"
+#include "scoped_thread_state_change.h"
+#include "thread-inl.h"
+#include "utils.h"
+
+namespace art {
+
+#if defined(__APPLE__)
+
+// This works on Mac OS 10.6 but hasn't been tested on older releases.
+struct __attribute__((__may_alias__)) darwin_pthread_mutex_t {
+ long padding0; // NOLINT(runtime/int) exact match to darwin type
+ int padding1;
+ uint32_t padding2;
+ int16_t padding3;
+ int16_t padding4;
+ uint32_t padding5;
+ pthread_t darwin_pthread_mutex_owner;
+ // ...other stuff we don't care about.
+};
+
+struct __attribute__((__may_alias__)) darwin_pthread_rwlock_t {
+ long padding0; // NOLINT(runtime/int) exact match to darwin type
+ pthread_mutex_t padding1;
+ int padding2;
+ pthread_cond_t padding3;
+ pthread_cond_t padding4;
+ int padding5;
+ int padding6;
+ pthread_t darwin_pthread_rwlock_owner;
+ // ...other stuff we don't care about.
+};
+
+#endif // __APPLE__
+
+#if defined(__GLIBC__)
+
+struct __attribute__((__may_alias__)) glibc_pthread_mutex_t {
+ int32_t padding0[2];
+ int owner;
+ // ...other stuff we don't care about.
+};
+
+struct __attribute__((__may_alias__)) glibc_pthread_rwlock_t {
+#ifdef __LP64__
+ int32_t padding0[6];
+#else
+ int32_t padding0[7];
+#endif
+ int writer;
+ // ...other stuff we don't care about.
+};
+
+#endif // __GLIBC__
+
+#if ART_USE_FUTEXES
+static bool ComputeRelativeTimeSpec(timespec* result_ts, const timespec& lhs, const timespec& rhs) {
+ const int32_t one_sec = 1000 * 1000 * 1000; // one second in nanoseconds.
+ result_ts->tv_sec = lhs.tv_sec - rhs.tv_sec;
+ result_ts->tv_nsec = lhs.tv_nsec - rhs.tv_nsec;
+ if (result_ts->tv_nsec < 0) {
+ result_ts->tv_sec--;
+ result_ts->tv_nsec += one_sec;
+ } else if (result_ts->tv_nsec > one_sec) {
+ result_ts->tv_sec++;
+ result_ts->tv_nsec -= one_sec;
+ }
+ return result_ts->tv_sec < 0;
+}
+#endif
+
+struct AllMutexData {
+ // A guard for all_mutexes_ that's not a mutex (Mutexes must CAS to acquire and busy wait).
+ AtomicInteger all_mutexes_guard;
+ // All created mutexes guarded by all_mutexes_guard_.
+ std::set<BaseMutex*>* all_mutexes;
+ AllMutexData() : all_mutexes(NULL) {}
+};
+static struct AllMutexData all_mutex_data[kAllMutexDataSize];
+
+class ScopedAllMutexesLock {
+ public:
+ explicit ScopedAllMutexesLock(const BaseMutex* mutex) : mutex_(mutex) {
+ while (!all_mutex_data->all_mutexes_guard.compare_and_swap(0, reinterpret_cast<int32_t>(mutex))) {
+ NanoSleep(100);
+ }
+ }
+ ~ScopedAllMutexesLock() {
+ while (!all_mutex_data->all_mutexes_guard.compare_and_swap(reinterpret_cast<int32_t>(mutex_), 0)) {
+ NanoSleep(100);
+ }
+ }
+ private:
+ const BaseMutex* const mutex_;
+};
+
+BaseMutex::BaseMutex(const char* name, LockLevel level) : level_(level), name_(name) {
+ if (kLogLockContentions) {
+ ScopedAllMutexesLock mu(this);
+ std::set<BaseMutex*>** all_mutexes_ptr = &all_mutex_data->all_mutexes;
+ if (*all_mutexes_ptr == NULL) {
+ // We leak the global set of all mutexes to avoid ordering issues in global variable
+ // construction/destruction.
+ *all_mutexes_ptr = new std::set<BaseMutex*>();
+ }
+ (*all_mutexes_ptr)->insert(this);
+ }
+}
+
+BaseMutex::~BaseMutex() {
+ if (kLogLockContentions) {
+ ScopedAllMutexesLock mu(this);
+ all_mutex_data->all_mutexes->erase(this);
+ }
+}
+
+void BaseMutex::DumpAll(std::ostream& os) {
+ if (kLogLockContentions) {
+ os << "Mutex logging:\n";
+ ScopedAllMutexesLock mu(reinterpret_cast<const BaseMutex*>(-1));
+ std::set<BaseMutex*>* all_mutexes = all_mutex_data->all_mutexes;
+ if (all_mutexes == NULL) {
+ // No mutexes have been created yet during at startup.
+ return;
+ }
+ typedef std::set<BaseMutex*>::const_iterator It;
+ os << "(Contented)\n";
+ for (It it = all_mutexes->begin(); it != all_mutexes->end(); ++it) {
+ BaseMutex* mutex = *it;
+ if (mutex->HasEverContended()) {
+ mutex->Dump(os);
+ os << "\n";
+ }
+ }
+ os << "(Never contented)\n";
+ for (It it = all_mutexes->begin(); it != all_mutexes->end(); ++it) {
+ BaseMutex* mutex = *it;
+ if (!mutex->HasEverContended()) {
+ mutex->Dump(os);
+ os << "\n";
+ }
+ }
+ }
+}
+
+void BaseMutex::CheckSafeToWait(Thread* self) {
+ if (self == NULL) {
+ CheckUnattachedThread(level_);
+ return;
+ }
+ if (kDebugLocking) {
+ CHECK(self->GetHeldMutex(level_) == this) << "Waiting on unacquired mutex: " << name_;
+ bool bad_mutexes_held = false;
+ for (int i = kLockLevelCount - 1; i >= 0; --i) {
+ if (i != level_) {
+ BaseMutex* held_mutex = self->GetHeldMutex(static_cast<LockLevel>(i));
+ if (held_mutex != NULL) {
+ LOG(ERROR) << "Holding \"" << held_mutex->name_ << "\" "
+ << "(level " << LockLevel(i) << ") while performing wait on "
+ << "\"" << name_ << "\" (level " << level_ << ")";
+ bad_mutexes_held = true;
+ }
+ }
+ }
+ CHECK(!bad_mutexes_held);
+ }
+}
+
+inline void BaseMutex::ContentionLogData::AddToWaitTime(uint64_t value) {
+ if (kLogLockContentions) {
+ // Atomically add value to wait_time.
+ uint64_t new_val, old_val;
+ volatile int64_t* addr = reinterpret_cast<volatile int64_t*>(&wait_time);
+ volatile const int64_t* caddr = const_cast<volatile const int64_t*>(addr);
+ do {
+ old_val = static_cast<uint64_t>(QuasiAtomic::Read64(caddr));
+ new_val = old_val + value;
+ } while (!QuasiAtomic::Cas64(static_cast<int64_t>(old_val), static_cast<int64_t>(new_val), addr));
+ }
+}
+
+void BaseMutex::RecordContention(uint64_t blocked_tid,
+ uint64_t owner_tid,
+ uint64_t nano_time_blocked) {
+ if (kLogLockContentions) {
+ ContentionLogData* data = contetion_log_data_;
+ ++(data->contention_count);
+ data->AddToWaitTime(nano_time_blocked);
+ ContentionLogEntry* log = data->contention_log;
+ // This code is intentionally racy as it is only used for diagnostics.
+ uint32_t slot = data->cur_content_log_entry;
+ if (log[slot].blocked_tid == blocked_tid &&
+ log[slot].owner_tid == blocked_tid) {
+ ++log[slot].count;
+ } else {
+ uint32_t new_slot;
+ do {
+ slot = data->cur_content_log_entry;
+ new_slot = (slot + 1) % kContentionLogSize;
+ } while (!data->cur_content_log_entry.compare_and_swap(slot, new_slot));
+ log[new_slot].blocked_tid = blocked_tid;
+ log[new_slot].owner_tid = owner_tid;
+ log[new_slot].count = 1;
+ }
+ }
+}
+
+void BaseMutex::DumpContention(std::ostream& os) const {
+ if (kLogLockContentions) {
+ const ContentionLogData* data = contetion_log_data_;
+ const ContentionLogEntry* log = data->contention_log;
+ uint64_t wait_time = data->wait_time;
+ uint32_t contention_count = data->contention_count;
+ if (contention_count == 0) {
+ os << "never contended";
+ } else {
+ os << "contended " << contention_count
+ << " times, average wait of contender " << PrettyDuration(wait_time / contention_count);
+ SafeMap<uint64_t, size_t> most_common_blocker;
+ SafeMap<uint64_t, size_t> most_common_blocked;
+ typedef SafeMap<uint64_t, size_t>::const_iterator It;
+ for (size_t i = 0; i < kContentionLogSize; ++i) {
+ uint64_t blocked_tid = log[i].blocked_tid;
+ uint64_t owner_tid = log[i].owner_tid;
+ uint32_t count = log[i].count;
+ if (count > 0) {
+ It it = most_common_blocked.find(blocked_tid);
+ if (it != most_common_blocked.end()) {
+ most_common_blocked.Overwrite(blocked_tid, it->second + count);
+ } else {
+ most_common_blocked.Put(blocked_tid, count);
+ }
+ it = most_common_blocker.find(owner_tid);
+ if (it != most_common_blocker.end()) {
+ most_common_blocker.Overwrite(owner_tid, it->second + count);
+ } else {
+ most_common_blocker.Put(owner_tid, count);
+ }
+ }
+ }
+ uint64_t max_tid = 0;
+ size_t max_tid_count = 0;
+ for (It it = most_common_blocked.begin(); it != most_common_blocked.end(); ++it) {
+ if (it->second > max_tid_count) {
+ max_tid = it->first;
+ max_tid_count = it->second;
+ }
+ }
+ if (max_tid != 0) {
+ os << " sample shows most blocked tid=" << max_tid;
+ }
+ max_tid = 0;
+ max_tid_count = 0;
+ for (It it = most_common_blocker.begin(); it != most_common_blocker.end(); ++it) {
+ if (it->second > max_tid_count) {
+ max_tid = it->first;
+ max_tid_count = it->second;
+ }
+ }
+ if (max_tid != 0) {
+ os << " sample shows tid=" << max_tid << " owning during this time";
+ }
+ }
+ }
+}
+
+
+Mutex::Mutex(const char* name, LockLevel level, bool recursive)
+ : BaseMutex(name, level), recursive_(recursive), recursion_count_(0) {
+#if ART_USE_FUTEXES
+ state_ = 0;
+ exclusive_owner_ = 0;
+ num_contenders_ = 0;
+#elif defined(__BIONIC__) || defined(__APPLE__)
+ // Use recursive mutexes for bionic and Apple otherwise the
+ // non-recursive mutexes don't have TIDs to check lock ownership of.
+ pthread_mutexattr_t attributes;
+ CHECK_MUTEX_CALL(pthread_mutexattr_init, (&attributes));
+ CHECK_MUTEX_CALL(pthread_mutexattr_settype, (&attributes, PTHREAD_MUTEX_RECURSIVE));
+ CHECK_MUTEX_CALL(pthread_mutex_init, (&mutex_, &attributes));
+ CHECK_MUTEX_CALL(pthread_mutexattr_destroy, (&attributes));
+#else
+ CHECK_MUTEX_CALL(pthread_mutex_init, (&mutex_, NULL));
+#endif
+}
+
+Mutex::~Mutex() {
+#if ART_USE_FUTEXES
+ if (state_ != 0) {
+ MutexLock mu(Thread::Current(), *Locks::runtime_shutdown_lock_);
+ Runtime* runtime = Runtime::Current();
+ bool shutting_down = (runtime == NULL) || runtime->IsShuttingDown();
+ LOG(shutting_down ? WARNING : FATAL) << "destroying mutex with owner: " << exclusive_owner_;
+ } else {
+ CHECK_EQ(exclusive_owner_, 0U) << "unexpectedly found an owner on unlocked mutex " << name_;
+ CHECK_EQ(num_contenders_, 0) << "unexpectedly found a contender on mutex " << name_;
+ }
+#else
+ // We can't use CHECK_MUTEX_CALL here because on shutdown a suspended daemon thread
+ // may still be using locks.
+ int rc = pthread_mutex_destroy(&mutex_);
+ if (rc != 0) {
+ errno = rc;
+ // TODO: should we just not log at all if shutting down? this could be the logging mutex!
+ MutexLock mu(Thread::Current(), *Locks::runtime_shutdown_lock_);
+ Runtime* runtime = Runtime::Current();
+ bool shutting_down = (runtime == NULL) || runtime->IsShuttingDown();
+ PLOG(shutting_down ? WARNING : FATAL) << "pthread_mutex_destroy failed for " << name_;
+ }
+#endif
+}
+
+void Mutex::ExclusiveLock(Thread* self) {
+ DCHECK(self == NULL || self == Thread::Current());
+ if (kDebugLocking && !recursive_) {
+ AssertNotHeld(self);
+ }
+ if (!recursive_ || !IsExclusiveHeld(self)) {
+#if ART_USE_FUTEXES
+ bool done = false;
+ do {
+ int32_t cur_state = state_;
+ if (cur_state == 0) {
+ // Change state from 0 to 1.
+ done = android_atomic_acquire_cas(0, 1, &state_) == 0;
+ } else {
+ // Failed to acquire, hang up.
+ ScopedContentionRecorder scr(this, SafeGetTid(self), GetExclusiveOwnerTid());
+ android_atomic_inc(&num_contenders_);
+ if (futex(&state_, FUTEX_WAIT, 1, NULL, NULL, 0) != 0) {
+ // EAGAIN and EINTR both indicate a spurious failure, try again from the beginning.
+ // We don't use TEMP_FAILURE_RETRY so we can intentionally retry to acquire the lock.
+ if ((errno != EAGAIN) && (errno != EINTR)) {
+ PLOG(FATAL) << "futex wait failed for " << name_;
+ }
+ }
+ android_atomic_dec(&num_contenders_);
+ }
+ } while (!done);
+ DCHECK_EQ(state_, 1);
+ exclusive_owner_ = SafeGetTid(self);
+#else
+ CHECK_MUTEX_CALL(pthread_mutex_lock, (&mutex_));
+#endif
+ RegisterAsLocked(self);
+ }
+ recursion_count_++;
+ if (kDebugLocking) {
+ CHECK(recursion_count_ == 1 || recursive_) << "Unexpected recursion count on mutex: "
+ << name_ << " " << recursion_count_;
+ AssertHeld(self);
+ }
+}
+
+bool Mutex::ExclusiveTryLock(Thread* self) {
+ DCHECK(self == NULL || self == Thread::Current());
+ if (kDebugLocking && !recursive_) {
+ AssertNotHeld(self);
+ }
+ if (!recursive_ || !IsExclusiveHeld(self)) {
+#if ART_USE_FUTEXES
+ bool done = false;
+ do {
+ int32_t cur_state = state_;
+ if (cur_state == 0) {
+ // Change state from 0 to 1.
+ done = android_atomic_acquire_cas(0, 1, &state_) == 0;
+ } else {
+ return false;
+ }
+ } while (!done);
+ DCHECK_EQ(state_, 1);
+ exclusive_owner_ = SafeGetTid(self);
+#else
+ int result = pthread_mutex_trylock(&mutex_);
+ if (result == EBUSY) {
+ return false;
+ }
+ if (result != 0) {
+ errno = result;
+ PLOG(FATAL) << "pthread_mutex_trylock failed for " << name_;
+ }
+#endif
+ RegisterAsLocked(self);
+ }
+ recursion_count_++;
+ if (kDebugLocking) {
+ CHECK(recursion_count_ == 1 || recursive_) << "Unexpected recursion count on mutex: "
+ << name_ << " " << recursion_count_;
+ AssertHeld(self);
+ }
+ return true;
+}
+
+void Mutex::ExclusiveUnlock(Thread* self) {
+ DCHECK(self == NULL || self == Thread::Current());
+ AssertHeld(self);
+ recursion_count_--;
+ if (!recursive_ || recursion_count_ == 0) {
+ if (kDebugLocking) {
+ CHECK(recursion_count_ == 0 || recursive_) << "Unexpected recursion count on mutex: "
+ << name_ << " " << recursion_count_;
+ }
+ RegisterAsUnlocked(self);
+#if ART_USE_FUTEXES
+ bool done = false;
+ do {
+ int32_t cur_state = state_;
+ if (cur_state == 1) {
+ // We're no longer the owner.
+ exclusive_owner_ = 0;
+ // Change state to 0.
+ done = android_atomic_release_cas(cur_state, 0, &state_) == 0;
+ if (done) { // Spurious fail?
+ // Wake a contender
+ if (num_contenders_ > 0) {
+ futex(&state_, FUTEX_WAKE, 1, NULL, NULL, 0);
+ }
+ }
+ } else {
+ // Logging acquires the logging lock, avoid infinite recursion in that case.
+ if (this != Locks::logging_lock_) {
+ LOG(FATAL) << "Unexpected state_ in unlock " << cur_state << " for " << name_;
+ } else {
+ LogMessageData data(__FILE__, __LINE__, INTERNAL_FATAL, -1);
+ LogMessage::LogLine(data, StringPrintf("Unexpected state_ %d in unlock for %s",
+ cur_state, name_).c_str());
+ _exit(1);
+ }
+ }
+ } while (!done);
+#else
+ CHECK_MUTEX_CALL(pthread_mutex_unlock, (&mutex_));
+#endif
+ }
+}
+
+bool Mutex::IsExclusiveHeld(const Thread* self) const {
+ DCHECK(self == NULL || self == Thread::Current());
+ bool result = (GetExclusiveOwnerTid() == SafeGetTid(self));
+ if (kDebugLocking) {
+ // Sanity debug check that if we think it is locked we have it in our held mutexes.
+ if (result && self != NULL && level_ != kMonitorLock && !gAborting) {
+ CHECK_EQ(self->GetHeldMutex(level_), this);
+ }
+ }
+ return result;
+}
+
+uint64_t Mutex::GetExclusiveOwnerTid() const {
+#if ART_USE_FUTEXES
+ return exclusive_owner_;
+#elif defined(__BIONIC__)
+ return static_cast<uint64_t>((mutex_.value >> 16) & 0xffff);
+#elif defined(__GLIBC__)
+ return reinterpret_cast<const glibc_pthread_mutex_t*>(&mutex_)->owner;
+#elif defined(__APPLE__)
+ const darwin_pthread_mutex_t* dpmutex = reinterpret_cast<const darwin_pthread_mutex_t*>(&mutex_);
+ pthread_t owner = dpmutex->darwin_pthread_mutex_owner;
+ // 0 for unowned, -1 for PTHREAD_MTX_TID_SWITCHING
+ // TODO: should we make darwin_pthread_mutex_owner volatile and recheck until not -1?
+ if ((owner == (pthread_t)0) || (owner == (pthread_t)-1)) {
+ return 0;
+ }
+ uint64_t tid;
+ CHECK_PTHREAD_CALL(pthread_threadid_np, (owner, &tid), __FUNCTION__); // Requires Mac OS 10.6
+ return tid;
+#else
+#error unsupported C library
+#endif
+}
+
+void Mutex::Dump(std::ostream& os) const {
+ os << (recursive_ ? "recursive " : "non-recursive ")
+ << name_
+ << " level=" << static_cast<int>(level_)
+ << " rec=" << recursion_count_
+ << " owner=" << GetExclusiveOwnerTid() << " ";
+ DumpContention(os);
+}
+
+std::ostream& operator<<(std::ostream& os, const Mutex& mu) {
+ mu.Dump(os);
+ return os;
+}
+
+ReaderWriterMutex::ReaderWriterMutex(const char* name, LockLevel level)
+ : BaseMutex(name, level)
+#if ART_USE_FUTEXES
+ , state_(0), exclusive_owner_(0), num_pending_readers_(0), num_pending_writers_(0)
+#endif
+{ // NOLINT(whitespace/braces)
+#if !ART_USE_FUTEXES
+ CHECK_MUTEX_CALL(pthread_rwlock_init, (&rwlock_, NULL));
+#endif
+}
+
+ReaderWriterMutex::~ReaderWriterMutex() {
+#if ART_USE_FUTEXES
+ CHECK_EQ(state_, 0);
+ CHECK_EQ(exclusive_owner_, 0U);
+ CHECK_EQ(num_pending_readers_, 0);
+ CHECK_EQ(num_pending_writers_, 0);
+#else
+ // We can't use CHECK_MUTEX_CALL here because on shutdown a suspended daemon thread
+ // may still be using locks.
+ int rc = pthread_rwlock_destroy(&rwlock_);
+ if (rc != 0) {
+ errno = rc;
+ // TODO: should we just not log at all if shutting down? this could be the logging mutex!
+ MutexLock mu(Thread::Current(), *Locks::runtime_shutdown_lock_);
+ Runtime* runtime = Runtime::Current();
+ bool shutting_down = runtime == NULL || runtime->IsShuttingDown();
+ PLOG(shutting_down ? WARNING : FATAL) << "pthread_rwlock_destroy failed for " << name_;
+ }
+#endif
+}
+
+void ReaderWriterMutex::ExclusiveLock(Thread* self) {
+ DCHECK(self == NULL || self == Thread::Current());
+ AssertNotExclusiveHeld(self);
+#if ART_USE_FUTEXES
+ bool done = false;
+ do {
+ int32_t cur_state = state_;
+ if (cur_state == 0) {
+ // Change state from 0 to -1.
+ done = android_atomic_acquire_cas(0, -1, &state_) == 0;
+ } else {
+ // Failed to acquire, hang up.
+ ScopedContentionRecorder scr(this, SafeGetTid(self), GetExclusiveOwnerTid());
+ android_atomic_inc(&num_pending_writers_);
+ if (futex(&state_, FUTEX_WAIT, cur_state, NULL, NULL, 0) != 0) {
+ // EAGAIN and EINTR both indicate a spurious failure, try again from the beginning.
+ // We don't use TEMP_FAILURE_RETRY so we can intentionally retry to acquire the lock.
+ if ((errno != EAGAIN) && (errno != EINTR)) {
+ PLOG(FATAL) << "futex wait failed for " << name_;
+ }
+ }
+ android_atomic_dec(&num_pending_writers_);
+ }
+ } while (!done);
+ DCHECK_EQ(state_, -1);
+ exclusive_owner_ = SafeGetTid(self);
+#else
+ CHECK_MUTEX_CALL(pthread_rwlock_wrlock, (&rwlock_));
+#endif
+ RegisterAsLocked(self);
+ AssertExclusiveHeld(self);
+}
+
+void ReaderWriterMutex::ExclusiveUnlock(Thread* self) {
+ DCHECK(self == NULL || self == Thread::Current());
+ AssertExclusiveHeld(self);
+ RegisterAsUnlocked(self);
+#if ART_USE_FUTEXES
+ bool done = false;
+ do {
+ int32_t cur_state = state_;
+ if (cur_state == -1) {
+ // We're no longer the owner.
+ exclusive_owner_ = 0;
+ // Change state from -1 to 0.
+ done = android_atomic_release_cas(-1, 0, &state_) == 0;
+ if (done) { // cmpxchg may fail due to noise?
+ // Wake any waiters.
+ if (num_pending_readers_ > 0 || num_pending_writers_ > 0) {
+ futex(&state_, FUTEX_WAKE, -1, NULL, NULL, 0);
+ }
+ }
+ } else {
+ LOG(FATAL) << "Unexpected state_:" << cur_state << " for " << name_;
+ }
+ } while (!done);
+#else
+ CHECK_MUTEX_CALL(pthread_rwlock_unlock, (&rwlock_));
+#endif
+}
+
+#if HAVE_TIMED_RWLOCK
+bool ReaderWriterMutex::ExclusiveLockWithTimeout(Thread* self, int64_t ms, int32_t ns) {
+ DCHECK(self == NULL || self == Thread::Current());
+#if ART_USE_FUTEXES
+ bool done = false;
+ timespec end_abs_ts;
+ InitTimeSpec(true, CLOCK_REALTIME, ms, ns, &end_abs_ts);
+ do {
+ int32_t cur_state = state_;
+ if (cur_state == 0) {
+ // Change state from 0 to -1.
+ done = android_atomic_acquire_cas(0, -1, &state_) == 0;
+ } else {
+ // Failed to acquire, hang up.
+ timespec now_abs_ts;
+ InitTimeSpec(true, CLOCK_REALTIME, 0, 0, &now_abs_ts);
+ timespec rel_ts;
+ if (ComputeRelativeTimeSpec(&rel_ts, end_abs_ts, now_abs_ts)) {
+ return false; // Timed out.
+ }
+ ScopedContentionRecorder scr(this, SafeGetTid(self), GetExclusiveOwnerTid());
+ android_atomic_inc(&num_pending_writers_);
+ if (futex(&state_, FUTEX_WAIT, cur_state, &rel_ts, NULL, 0) != 0) {
+ if (errno == ETIMEDOUT) {
+ android_atomic_dec(&num_pending_writers_);
+ return false; // Timed out.
+ } else if ((errno != EAGAIN) && (errno != EINTR)) {
+ // EAGAIN and EINTR both indicate a spurious failure,
+ // recompute the relative time out from now and try again.
+ // We don't use TEMP_FAILURE_RETRY so we can recompute rel_ts;
+ PLOG(FATAL) << "timed futex wait failed for " << name_;
+ }
+ }
+ android_atomic_dec(&num_pending_writers_);
+ }
+ } while (!done);
+ exclusive_owner_ = SafeGetTid(self);
+#else
+ timespec ts;
+ InitTimeSpec(true, CLOCK_REALTIME, ms, ns, &ts);
+ int result = pthread_rwlock_timedwrlock(&rwlock_, &ts);
+ if (result == ETIMEDOUT) {
+ return false;
+ }
+ if (result != 0) {
+ errno = result;
+ PLOG(FATAL) << "pthread_rwlock_timedwrlock failed for " << name_;
+ }
+#endif
+ RegisterAsLocked(self);
+ AssertSharedHeld(self);
+ return true;
+}
+#endif
+
+bool ReaderWriterMutex::SharedTryLock(Thread* self) {
+ DCHECK(self == NULL || self == Thread::Current());
+#if ART_USE_FUTEXES
+ bool done = false;
+ do {
+ int32_t cur_state = state_;
+ if (cur_state >= 0) {
+ // Add as an extra reader.
+ done = android_atomic_acquire_cas(cur_state, cur_state + 1, &state_) == 0;
+ } else {
+ // Owner holds it exclusively.
+ return false;
+ }
+ } while (!done);
+#else
+ int result = pthread_rwlock_tryrdlock(&rwlock_);
+ if (result == EBUSY) {
+ return false;
+ }
+ if (result != 0) {
+ errno = result;
+ PLOG(FATAL) << "pthread_mutex_trylock failed for " << name_;
+ }
+#endif
+ RegisterAsLocked(self);
+ AssertSharedHeld(self);
+ return true;
+}
+
+bool ReaderWriterMutex::IsExclusiveHeld(const Thread* self) const {
+ DCHECK(self == NULL || self == Thread::Current());
+ bool result = (GetExclusiveOwnerTid() == SafeGetTid(self));
+ if (kDebugLocking) {
+ // Sanity that if the pthread thinks we own the lock the Thread agrees.
+ if (self != NULL && result) {
+ CHECK_EQ(self->GetHeldMutex(level_), this);
+ }
+ }
+ return result;
+}
+
+bool ReaderWriterMutex::IsSharedHeld(const Thread* self) const {
+ DCHECK(self == NULL || self == Thread::Current());
+ bool result;
+ if (UNLIKELY(self == NULL)) { // Handle unattached threads.
+ result = IsExclusiveHeld(self); // TODO: a better best effort here.
+ } else {
+ result = (self->GetHeldMutex(level_) == this);
+ }
+ return result;
+}
+
+uint64_t ReaderWriterMutex::GetExclusiveOwnerTid() const {
+#if ART_USE_FUTEXES
+ int32_t state = state_;
+ if (state == 0) {
+ return 0; // No owner.
+ } else if (state > 0) {
+ return -1; // Shared.
+ } else {
+ return exclusive_owner_;
+ }
+#else
+#if defined(__BIONIC__)
+ return rwlock_.writerThreadId;
+#elif defined(__GLIBC__)
+ return reinterpret_cast<const glibc_pthread_rwlock_t*>(&rwlock_)->writer;
+#elif defined(__APPLE__)
+ const darwin_pthread_rwlock_t*
+ dprwlock = reinterpret_cast<const darwin_pthread_rwlock_t*>(&rwlock_);
+ pthread_t owner = dprwlock->darwin_pthread_rwlock_owner;
+ if (owner == (pthread_t)0) {
+ return 0;
+ }
+ uint64_t tid;
+ CHECK_PTHREAD_CALL(pthread_threadid_np, (owner, &tid), __FUNCTION__); // Requires Mac OS 10.6
+ return tid;
+#else
+#error unsupported C library
+#endif
+#endif
+}
+
+void ReaderWriterMutex::Dump(std::ostream& os) const {
+ os << name_
+ << " level=" << static_cast<int>(level_)
+ << " owner=" << GetExclusiveOwnerTid() << " ";
+ DumpContention(os);
+}
+
+std::ostream& operator<<(std::ostream& os, const ReaderWriterMutex& mu) {
+ mu.Dump(os);
+ return os;
+}
+
+ConditionVariable::ConditionVariable(const char* name, Mutex& guard)
+ : name_(name), guard_(guard) {
+#if ART_USE_FUTEXES
+ sequence_ = 0;
+ num_waiters_ = 0;
+#else
+ CHECK_MUTEX_CALL(pthread_cond_init, (&cond_, NULL));
+#endif
+}
+
+ConditionVariable::~ConditionVariable() {
+#if ART_USE_FUTEXES
+ if (num_waiters_!= 0) {
+ MutexLock mu(Thread::Current(), *Locks::runtime_shutdown_lock_);
+ Runtime* runtime = Runtime::Current();
+ bool shutting_down = (runtime == NULL) || runtime->IsShuttingDown();
+ LOG(shutting_down ? WARNING : FATAL) << "ConditionVariable::~ConditionVariable for " << name_
+ << " called with " << num_waiters_ << " waiters.";
+ }
+#else
+ // We can't use CHECK_MUTEX_CALL here because on shutdown a suspended daemon thread
+ // may still be using condition variables.
+ int rc = pthread_cond_destroy(&cond_);
+ if (rc != 0) {
+ errno = rc;
+ MutexLock mu(Thread::Current(), *Locks::runtime_shutdown_lock_);
+ Runtime* runtime = Runtime::Current();
+ bool shutting_down = (runtime == NULL) || runtime->IsShuttingDown();
+ PLOG(shutting_down ? WARNING : FATAL) << "pthread_cond_destroy failed for " << name_;
+ }
+#endif
+}
+
+void ConditionVariable::Broadcast(Thread* self) {
+ DCHECK(self == NULL || self == Thread::Current());
+ // TODO: enable below, there's a race in thread creation that causes false failures currently.
+ // guard_.AssertExclusiveHeld(self);
+ DCHECK_EQ(guard_.GetExclusiveOwnerTid(), SafeGetTid(self));
+#if ART_USE_FUTEXES
+ if (num_waiters_ > 0) {
+ android_atomic_inc(&sequence_); // Indicate the broadcast occurred.
+ bool done = false;
+ do {
+ int32_t cur_sequence = sequence_;
+ // Requeue waiters onto mutex. The waiter holds the contender count on the mutex high ensuring
+ // mutex unlocks will awaken the requeued waiter thread.
+ done = futex(&sequence_, FUTEX_CMP_REQUEUE, 0,
+ reinterpret_cast<const timespec*>(std::numeric_limits<int32_t>::max()),
+ &guard_.state_, cur_sequence) != -1;
+ if (!done) {
+ if (errno != EAGAIN) {
+ PLOG(FATAL) << "futex cmp requeue failed for " << name_;
+ }
+ }
+ } while (!done);
+ }
+#else
+ CHECK_MUTEX_CALL(pthread_cond_broadcast, (&cond_));
+#endif
+}
+
+void ConditionVariable::Signal(Thread* self) {
+ DCHECK(self == NULL || self == Thread::Current());
+ guard_.AssertExclusiveHeld(self);
+#if ART_USE_FUTEXES
+ if (num_waiters_ > 0) {
+ android_atomic_inc(&sequence_); // Indicate a signal occurred.
+ // Futex wake 1 waiter who will then come and in contend on mutex. It'd be nice to requeue them
+ // to avoid this, however, requeueing can only move all waiters.
+ int num_woken = futex(&sequence_, FUTEX_WAKE, 1, NULL, NULL, 0);
+ // Check something was woken or else we changed sequence_ before they had chance to wait.
+ CHECK((num_woken == 0) || (num_woken == 1));
+ }
+#else
+ CHECK_MUTEX_CALL(pthread_cond_signal, (&cond_));
+#endif
+}
+
+void ConditionVariable::Wait(Thread* self) {
+ guard_.CheckSafeToWait(self);
+ WaitHoldingLocks(self);
+}
+
+void ConditionVariable::WaitHoldingLocks(Thread* self) {
+ DCHECK(self == NULL || self == Thread::Current());
+ guard_.AssertExclusiveHeld(self);
+ unsigned int old_recursion_count = guard_.recursion_count_;
+#if ART_USE_FUTEXES
+ num_waiters_++;
+ // Ensure the Mutex is contended so that requeued threads are awoken.
+ android_atomic_inc(&guard_.num_contenders_);
+ guard_.recursion_count_ = 1;
+ int32_t cur_sequence = sequence_;
+ guard_.ExclusiveUnlock(self);
+ if (futex(&sequence_, FUTEX_WAIT, cur_sequence, NULL, NULL, 0) != 0) {
+ // Futex failed, check it is an expected error.
+ // EAGAIN == EWOULDBLK, so we let the caller try again.
+ // EINTR implies a signal was sent to this thread.
+ if ((errno != EINTR) && (errno != EAGAIN)) {
+ PLOG(FATAL) << "futex wait failed for " << name_;
+ }
+ }
+ guard_.ExclusiveLock(self);
+ CHECK_GE(num_waiters_, 0);
+ num_waiters_--;
+ // We awoke and so no longer require awakes from the guard_'s unlock.
+ CHECK_GE(guard_.num_contenders_, 0);
+ android_atomic_dec(&guard_.num_contenders_);
+#else
+ guard_.recursion_count_ = 0;
+ CHECK_MUTEX_CALL(pthread_cond_wait, (&cond_, &guard_.mutex_));
+#endif
+ guard_.recursion_count_ = old_recursion_count;
+}
+
+void ConditionVariable::TimedWait(Thread* self, int64_t ms, int32_t ns) {
+ DCHECK(self == NULL || self == Thread::Current());
+ guard_.AssertExclusiveHeld(self);
+ guard_.CheckSafeToWait(self);
+ unsigned int old_recursion_count = guard_.recursion_count_;
+#if ART_USE_FUTEXES
+ timespec rel_ts;
+ InitTimeSpec(false, CLOCK_REALTIME, ms, ns, &rel_ts);
+ num_waiters_++;
+ // Ensure the Mutex is contended so that requeued threads are awoken.
+ android_atomic_inc(&guard_.num_contenders_);
+ guard_.recursion_count_ = 1;
+ int32_t cur_sequence = sequence_;
+ guard_.ExclusiveUnlock(self);
+ if (futex(&sequence_, FUTEX_WAIT, cur_sequence, &rel_ts, NULL, 0) != 0) {
+ if (errno == ETIMEDOUT) {
+ // Timed out we're done.
+ } else if ((errno == EAGAIN) || (errno == EINTR)) {
+ // A signal or ConditionVariable::Signal/Broadcast has come in.
+ } else {
+ PLOG(FATAL) << "timed futex wait failed for " << name_;
+ }
+ }
+ guard_.ExclusiveLock(self);
+ CHECK_GE(num_waiters_, 0);
+ num_waiters_--;
+ // We awoke and so no longer require awakes from the guard_'s unlock.
+ CHECK_GE(guard_.num_contenders_, 0);
+ android_atomic_dec(&guard_.num_contenders_);
+#else
+#ifdef HAVE_TIMEDWAIT_MONOTONIC
+#define TIMEDWAIT pthread_cond_timedwait_monotonic
+ int clock = CLOCK_MONOTONIC;
+#else
+#define TIMEDWAIT pthread_cond_timedwait
+ int clock = CLOCK_REALTIME;
+#endif
+ guard_.recursion_count_ = 0;
+ timespec ts;
+ InitTimeSpec(true, clock, ms, ns, &ts);
+ int rc = TEMP_FAILURE_RETRY(TIMEDWAIT(&cond_, &guard_.mutex_, &ts));
+ if (rc != 0 && rc != ETIMEDOUT) {
+ errno = rc;
+ PLOG(FATAL) << "TimedWait failed for " << name_;
+ }
+#endif
+ guard_.recursion_count_ = old_recursion_count;
+}
+
+} // namespace art
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
new file mode 100644
index 0000000..ee37388
--- /dev/null
+++ b/runtime/base/mutex.h
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_BASE_MUTEX_H_
+#define ART_RUNTIME_BASE_MUTEX_H_
+
+#include <pthread.h>
+#include <stdint.h>
+
+#include <iosfwd>
+#include <string>
+
+#include "atomic_integer.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "globals.h"
+#include "locks.h"
+
+#if defined(__APPLE__)
+#define ART_USE_FUTEXES 0
+#else
+#define ART_USE_FUTEXES !defined(__mips__)
+#endif
+
+// Currently Darwin doesn't support locks with timeouts.
+#if !defined(__APPLE__)
+#define HAVE_TIMED_RWLOCK 1
+#else
+#define HAVE_TIMED_RWLOCK 0
+#endif
+
+namespace art {
+
+class ScopedContentionRecorder;
+class Thread;
+
+const bool kDebugLocking = kIsDebugBuild;
+
+// Record Log contention information, dumpable via SIGQUIT.
+#ifdef ART_USE_FUTEXES
+// To enable lock contention logging, set this to true.
+const bool kLogLockContentions = false;
+#else
+// Keep this false as lock contention logging is supported only with
+// futex.
+const bool kLogLockContentions = false;
+#endif
+const size_t kContentionLogSize = 64;
+const size_t kContentionLogDataSize = kLogLockContentions ? 1 : 0;
+const size_t kAllMutexDataSize = kLogLockContentions ? 1 : 0;
+
+// Base class for all Mutex implementations
+class BaseMutex {
+ public:
+ const char* GetName() const {
+ return name_;
+ }
+
+ virtual bool IsMutex() const { return false; }
+ virtual bool IsReaderWriterMutex() const { return false; }
+
+ virtual void Dump(std::ostream& os) const = 0;
+
+ static void DumpAll(std::ostream& os);
+
+ protected:
+ friend class ConditionVariable;
+
+ BaseMutex(const char* name, LockLevel level);
+ virtual ~BaseMutex();
+ void RegisterAsLocked(Thread* self);
+ void RegisterAsUnlocked(Thread* self);
+ void CheckSafeToWait(Thread* self);
+
+ friend class ScopedContentionRecorder;
+
+ void RecordContention(uint64_t blocked_tid, uint64_t owner_tid, uint64_t nano_time_blocked);
+ void DumpContention(std::ostream& os) const;
+
+ const LockLevel level_; // Support for lock hierarchy.
+ const char* const name_;
+
+ // A log entry that records contention but makes no guarantee that either tid will be held live.
+ struct ContentionLogEntry {
+ ContentionLogEntry() : blocked_tid(0), owner_tid(0) {}
+ uint64_t blocked_tid;
+ uint64_t owner_tid;
+ AtomicInteger count;
+ };
+ struct ContentionLogData {
+ ContentionLogEntry contention_log[kContentionLogSize];
+ // The next entry in the contention log to be updated. Value ranges from 0 to
+ // kContentionLogSize - 1.
+ AtomicInteger cur_content_log_entry;
+ // Number of times the Mutex has been contended.
+ AtomicInteger contention_count;
+ // Sum of time waited by all contenders in ns.
+ volatile uint64_t wait_time;
+ void AddToWaitTime(uint64_t value);
+ ContentionLogData() : wait_time(0) {}
+ };
+ ContentionLogData contetion_log_data_[kContentionLogDataSize];
+
+ public:
+ bool HasEverContended() const {
+ if (kLogLockContentions) {
+ return contetion_log_data_->contention_count > 0;
+ }
+ return false;
+ }
+};
+
+// A Mutex is used to achieve mutual exclusion between threads. A Mutex can be used to gain
+// exclusive access to what it guards. A Mutex can be in one of two states:
+// - Free - not owned by any thread,
+// - Exclusive - owned by a single thread.
+//
+// The effect of locking and unlocking operations on the state is:
+// State | ExclusiveLock | ExclusiveUnlock
+// -------------------------------------------
+// Free | Exclusive | error
+// Exclusive | Block* | Free
+// * Mutex is not reentrant and so an attempt to ExclusiveLock on the same thread will result in
+// an error. Being non-reentrant simplifies Waiting on ConditionVariables.
+std::ostream& operator<<(std::ostream& os, const Mutex& mu);
+class LOCKABLE Mutex : public BaseMutex {
+ public:
+ explicit Mutex(const char* name, LockLevel level = kDefaultMutexLevel, bool recursive = false);
+ ~Mutex();
+
+ virtual bool IsMutex() const { return true; }
+
+ // Block until mutex is free then acquire exclusive access.
+ void ExclusiveLock(Thread* self) EXCLUSIVE_LOCK_FUNCTION();
+ void Lock(Thread* self) EXCLUSIVE_LOCK_FUNCTION() { ExclusiveLock(self); }
+
+ // Returns true if acquires exclusive access, false otherwise.
+ bool ExclusiveTryLock(Thread* self) EXCLUSIVE_TRYLOCK_FUNCTION(true);
+ bool TryLock(Thread* self) EXCLUSIVE_TRYLOCK_FUNCTION(true) { return ExclusiveTryLock(self); }
+
+ // Release exclusive access.
+ void ExclusiveUnlock(Thread* self) UNLOCK_FUNCTION();
+ void Unlock(Thread* self) UNLOCK_FUNCTION() { ExclusiveUnlock(self); }
+
+ // Is the current thread the exclusive holder of the Mutex.
+ bool IsExclusiveHeld(const Thread* self) const;
+
+ // Assert that the Mutex is exclusively held by the current thread.
+ void AssertExclusiveHeld(const Thread* self) {
+ if (kDebugLocking && (gAborting == 0)) {
+ CHECK(IsExclusiveHeld(self)) << *this;
+ }
+ }
+ void AssertHeld(const Thread* self) { AssertExclusiveHeld(self); }
+
+ // Assert that the Mutex is not held by the current thread.
+ void AssertNotHeldExclusive(const Thread* self) {
+ if (kDebugLocking && (gAborting == 0)) {
+ CHECK(!IsExclusiveHeld(self)) << *this;
+ }
+ }
+ void AssertNotHeld(const Thread* self) { AssertNotHeldExclusive(self); }
+
+ // Id associated with exclusive owner.
+ uint64_t GetExclusiveOwnerTid() const;
+
+ // Returns how many times this Mutex has been locked, it is better to use AssertHeld/NotHeld.
+ unsigned int GetDepth() const {
+ return recursion_count_;
+ }
+
+ virtual void Dump(std::ostream& os) const;
+
+ private:
+#if ART_USE_FUTEXES
+ // 0 is unheld, 1 is held.
+ volatile int32_t state_;
+ // Exclusive owner.
+ volatile uint64_t exclusive_owner_;
+ // Number of waiting contenders.
+ volatile int32_t num_contenders_;
+#else
+ pthread_mutex_t mutex_;
+#endif
+ const bool recursive_; // Can the lock be recursively held?
+ unsigned int recursion_count_;
+ friend class ConditionVariable;
+ DISALLOW_COPY_AND_ASSIGN(Mutex);
+};
+
+// A ReaderWriterMutex is used to achieve mutual exclusion between threads, similar to a Mutex.
+// Unlike a Mutex a ReaderWriterMutex can be used to gain exclusive (writer) or shared (reader)
+// access to what it guards. A flaw in relation to a Mutex is that it cannot be used with a
+// condition variable. A ReaderWriterMutex can be in one of three states:
+// - Free - not owned by any thread,
+// - Exclusive - owned by a single thread,
+// - Shared(n) - shared amongst n threads.
+//
+// The effect of locking and unlocking operations on the state is:
+//
+// State | ExclusiveLock | ExclusiveUnlock | SharedLock | SharedUnlock
+// ----------------------------------------------------------------------------
+// Free | Exclusive | error | SharedLock(1) | error
+// Exclusive | Block | Free | Block | error
+// Shared(n) | Block | error | SharedLock(n+1)* | Shared(n-1) or Free
+// * for large values of n the SharedLock may block.
+std::ostream& operator<<(std::ostream& os, const ReaderWriterMutex& mu);
+class LOCKABLE ReaderWriterMutex : public BaseMutex {
+ public:
+ explicit ReaderWriterMutex(const char* name, LockLevel level = kDefaultMutexLevel);
+ ~ReaderWriterMutex();
+
+ virtual bool IsReaderWriterMutex() const { return true; }
+
+ // Block until ReaderWriterMutex is free then acquire exclusive access.
+ void ExclusiveLock(Thread* self) EXCLUSIVE_LOCK_FUNCTION();
+ void WriterLock(Thread* self) EXCLUSIVE_LOCK_FUNCTION() { ExclusiveLock(self); }
+
+ // Release exclusive access.
+ void ExclusiveUnlock(Thread* self) UNLOCK_FUNCTION();
+ void WriterUnlock(Thread* self) UNLOCK_FUNCTION() { ExclusiveUnlock(self); }
+
+ // Block until ReaderWriterMutex is free and acquire exclusive access. Returns true on success
+ // or false if timeout is reached.
+#if HAVE_TIMED_RWLOCK
+ bool ExclusiveLockWithTimeout(Thread* self, int64_t ms, int32_t ns)
+ EXCLUSIVE_TRYLOCK_FUNCTION(true);
+#endif
+
+ // Block until ReaderWriterMutex is shared or free then acquire a share on the access.
+ void SharedLock(Thread* self) SHARED_LOCK_FUNCTION() ALWAYS_INLINE;
+ void ReaderLock(Thread* self) SHARED_LOCK_FUNCTION() { SharedLock(self); }
+
+ // Try to acquire share of ReaderWriterMutex.
+ bool SharedTryLock(Thread* self) EXCLUSIVE_TRYLOCK_FUNCTION(true);
+
+ // Release a share of the access.
+ void SharedUnlock(Thread* self) UNLOCK_FUNCTION() ALWAYS_INLINE;
+ void ReaderUnlock(Thread* self) UNLOCK_FUNCTION() { SharedUnlock(self); }
+
+ // Is the current thread the exclusive holder of the ReaderWriterMutex.
+ bool IsExclusiveHeld(const Thread* self) const;
+
+ // Assert the current thread has exclusive access to the ReaderWriterMutex.
+ void AssertExclusiveHeld(const Thread* self) {
+ if (kDebugLocking && (gAborting == 0)) {
+ CHECK(IsExclusiveHeld(self)) << *this;
+ }
+ }
+ void AssertWriterHeld(const Thread* self) { AssertExclusiveHeld(self); }
+
+ // Assert the current thread doesn't have exclusive access to the ReaderWriterMutex.
+ void AssertNotExclusiveHeld(const Thread* self) {
+ if (kDebugLocking && (gAborting == 0)) {
+ CHECK(!IsExclusiveHeld(self)) << *this;
+ }
+ }
+ void AssertNotWriterHeld(const Thread* self) { AssertNotExclusiveHeld(self); }
+
+ // Is the current thread a shared holder of the ReaderWriterMutex.
+ bool IsSharedHeld(const Thread* self) const;
+
+ // Assert the current thread has shared access to the ReaderWriterMutex.
+ void AssertSharedHeld(const Thread* self) {
+ if (kDebugLocking && (gAborting == 0)) {
+ // TODO: we can only assert this well when self != NULL.
+ CHECK(IsSharedHeld(self) || self == NULL) << *this;
+ }
+ }
+ void AssertReaderHeld(const Thread* self) { AssertSharedHeld(self); }
+
+ // Assert the current thread doesn't hold this ReaderWriterMutex either in shared or exclusive
+ // mode.
+ void AssertNotHeld(const Thread* self) {
+ if (kDebugLocking && (gAborting == 0)) {
+ CHECK(!IsSharedHeld(self)) << *this;
+ }
+ }
+
+ // Id associated with exclusive owner.
+ uint64_t GetExclusiveOwnerTid() const;
+
+ virtual void Dump(std::ostream& os) const;
+
+ private:
+#if ART_USE_FUTEXES
+ // -1 implies held exclusive, +ve shared held by state_ many owners.
+ volatile int32_t state_;
+ // Exclusive owner.
+ volatile uint64_t exclusive_owner_;
+ // Pending readers.
+ volatile int32_t num_pending_readers_;
+ // Pending writers.
+ volatile int32_t num_pending_writers_;
+#else
+ pthread_rwlock_t rwlock_;
+#endif
+ DISALLOW_COPY_AND_ASSIGN(ReaderWriterMutex);
+};
+
+// ConditionVariables allow threads to queue and sleep. Threads may then be resumed individually
+// (Signal) or all at once (Broadcast).
+class ConditionVariable {
+ public:
+ explicit ConditionVariable(const char* name, Mutex& mutex);
+ ~ConditionVariable();
+
+ void Broadcast(Thread* self);
+ void Signal(Thread* self);
+ // TODO: No thread safety analysis on Wait and TimedWait as they call mutex operations via their
+ // pointer copy, thereby defeating annotalysis.
+ void Wait(Thread* self) NO_THREAD_SAFETY_ANALYSIS;
+ void TimedWait(Thread* self, int64_t ms, int32_t ns) NO_THREAD_SAFETY_ANALYSIS;
+ // Variant of Wait that should be used with caution. Doesn't validate that no mutexes are held
+ // when waiting.
+ // TODO: remove this.
+ void WaitHoldingLocks(Thread* self) NO_THREAD_SAFETY_ANALYSIS;
+
+ private:
+ const char* const name_;
+ // The Mutex being used by waiters. It is an error to mix condition variables between different
+ // Mutexes.
+ Mutex& guard_;
+#if ART_USE_FUTEXES
+ // A counter that is modified by signals and broadcasts. This ensures that when a waiter gives up
+ // their Mutex and another thread takes it and signals, the waiting thread observes that sequence_
+ // changed and doesn't enter the wait. Modified while holding guard_, but is read by futex wait
+ // without guard_ held.
+ volatile int32_t sequence_;
+ // Number of threads that have come into to wait, not the length of the waiters on the futex as
+ // waiters may have been requeued onto guard_. Guarded by guard_.
+ volatile int32_t num_waiters_;
+#else
+ pthread_cond_t cond_;
+#endif
+ DISALLOW_COPY_AND_ASSIGN(ConditionVariable);
+};
+
+// Scoped locker/unlocker for a regular Mutex that acquires mu upon construction and releases it
+// upon destruction.
+class SCOPED_LOCKABLE MutexLock {
+ public:
+ explicit MutexLock(Thread* self, Mutex& mu) EXCLUSIVE_LOCK_FUNCTION(mu) : self_(self), mu_(mu) {
+ mu_.ExclusiveLock(self_);
+ }
+
+ ~MutexLock() UNLOCK_FUNCTION() {
+ mu_.ExclusiveUnlock(self_);
+ }
+
+ private:
+ Thread* const self_;
+ Mutex& mu_;
+ DISALLOW_COPY_AND_ASSIGN(MutexLock);
+};
+// Catch bug where variable name is omitted. "MutexLock (lock);" instead of "MutexLock mu(lock)".
+#define MutexLock(x) COMPILE_ASSERT(0, mutex_lock_declaration_missing_variable_name)
+
+// Scoped locker/unlocker for a ReaderWriterMutex that acquires read access to mu upon
+// construction and releases it upon destruction.
+class SCOPED_LOCKABLE ReaderMutexLock {
+ public:
+ explicit ReaderMutexLock(Thread* self, ReaderWriterMutex& mu) EXCLUSIVE_LOCK_FUNCTION(mu) :
+ self_(self), mu_(mu) {
+ mu_.SharedLock(self_);
+ }
+
+ ~ReaderMutexLock() UNLOCK_FUNCTION() {
+ mu_.SharedUnlock(self_);
+ }
+
+ private:
+ Thread* const self_;
+ ReaderWriterMutex& mu_;
+ DISALLOW_COPY_AND_ASSIGN(ReaderMutexLock);
+};
+// Catch bug where variable name is omitted. "ReaderMutexLock (lock);" instead of
+// "ReaderMutexLock mu(lock)".
+#define ReaderMutexLock(x) COMPILE_ASSERT(0, reader_mutex_lock_declaration_missing_variable_name)
+
+// Scoped locker/unlocker for a ReaderWriterMutex that acquires write access to mu upon
+// construction and releases it upon destruction.
+class SCOPED_LOCKABLE WriterMutexLock {
+ public:
+ explicit WriterMutexLock(Thread* self, ReaderWriterMutex& mu) EXCLUSIVE_LOCK_FUNCTION(mu) :
+ self_(self), mu_(mu) {
+ mu_.ExclusiveLock(self_);
+ }
+
+ ~WriterMutexLock() UNLOCK_FUNCTION() {
+ mu_.ExclusiveUnlock(self_);
+ }
+
+ private:
+ Thread* const self_;
+ ReaderWriterMutex& mu_;
+ DISALLOW_COPY_AND_ASSIGN(WriterMutexLock);
+};
+// Catch bug where variable name is omitted. "WriterMutexLock (lock);" instead of
+// "WriterMutexLock mu(lock)".
+#define WriterMutexLock(x) COMPILE_ASSERT(0, writer_mutex_lock_declaration_missing_variable_name)
+
+} // namespace art
+
+#endif // ART_RUNTIME_BASE_MUTEX_H_
diff --git a/runtime/base/mutex_test.cc b/runtime/base/mutex_test.cc
new file mode 100644
index 0000000..1af8e0a
--- /dev/null
+++ b/runtime/base/mutex_test.cc
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mutex.h"
+
+#include "common_test.h"
+
+namespace art {
+
+class MutexTest : public CommonTest {};
+
+struct MutexTester {
+ static void AssertDepth(Mutex& mu, uint32_t expected_depth) {
+ ASSERT_EQ(expected_depth, mu.GetDepth());
+
+ // This test is single-threaded, so we also know _who_ should hold the lock.
+ if (expected_depth == 0) {
+ mu.AssertNotHeld(Thread::Current());
+ } else {
+ mu.AssertHeld(Thread::Current());
+ }
+ }
+};
+
+TEST_F(MutexTest, LockUnlock) {
+ Mutex mu("test mutex");
+ MutexTester::AssertDepth(mu, 0U);
+ mu.Lock(Thread::Current());
+ MutexTester::AssertDepth(mu, 1U);
+ mu.Unlock(Thread::Current());
+ MutexTester::AssertDepth(mu, 0U);
+}
+
+// GCC has trouble with our mutex tests, so we have to turn off thread safety analysis.
+static void TryLockUnlockTest() NO_THREAD_SAFETY_ANALYSIS {
+ Mutex mu("test mutex");
+ MutexTester::AssertDepth(mu, 0U);
+ ASSERT_TRUE(mu.TryLock(Thread::Current()));
+ MutexTester::AssertDepth(mu, 1U);
+ mu.Unlock(Thread::Current());
+ MutexTester::AssertDepth(mu, 0U);
+}
+
+TEST_F(MutexTest, TryLockUnlock) {
+ TryLockUnlockTest();
+}
+
+// GCC has trouble with our mutex tests, so we have to turn off thread safety analysis.
+static void RecursiveLockUnlockTest() NO_THREAD_SAFETY_ANALYSIS {
+ Mutex mu("test mutex", kDefaultMutexLevel, true);
+ MutexTester::AssertDepth(mu, 0U);
+ mu.Lock(Thread::Current());
+ MutexTester::AssertDepth(mu, 1U);
+ mu.Lock(Thread::Current());
+ MutexTester::AssertDepth(mu, 2U);
+ mu.Unlock(Thread::Current());
+ MutexTester::AssertDepth(mu, 1U);
+ mu.Unlock(Thread::Current());
+ MutexTester::AssertDepth(mu, 0U);
+}
+
+TEST_F(MutexTest, RecursiveLockUnlock) {
+ RecursiveLockUnlockTest();
+}
+
+// GCC has trouble with our mutex tests, so we have to turn off thread safety analysis.
+static void RecursiveTryLockUnlockTest() NO_THREAD_SAFETY_ANALYSIS {
+ Mutex mu("test mutex", kDefaultMutexLevel, true);
+ MutexTester::AssertDepth(mu, 0U);
+ ASSERT_TRUE(mu.TryLock(Thread::Current()));
+ MutexTester::AssertDepth(mu, 1U);
+ ASSERT_TRUE(mu.TryLock(Thread::Current()));
+ MutexTester::AssertDepth(mu, 2U);
+ mu.Unlock(Thread::Current());
+ MutexTester::AssertDepth(mu, 1U);
+ mu.Unlock(Thread::Current());
+ MutexTester::AssertDepth(mu, 0U);
+}
+
+TEST_F(MutexTest, RecursiveTryLockUnlock) {
+ RecursiveTryLockUnlockTest();
+}
+
+
+struct RecursiveLockWait {
+ explicit RecursiveLockWait()
+ : mu("test mutex", kDefaultMutexLevel, true), cv("test condition variable", mu) {
+ }
+
+ static void* Callback(void* arg) {
+ RecursiveLockWait* state = reinterpret_cast<RecursiveLockWait*>(arg);
+ state->mu.Lock(Thread::Current());
+ state->cv.Signal(Thread::Current());
+ state->mu.Unlock(Thread::Current());
+ return NULL;
+ }
+
+ Mutex mu;
+ ConditionVariable cv;
+};
+
+// GCC has trouble with our mutex tests, so we have to turn off thread safety analysis.
+static void RecursiveLockWaitTest() NO_THREAD_SAFETY_ANALYSIS {
+ RecursiveLockWait state;
+ state.mu.Lock(Thread::Current());
+ state.mu.Lock(Thread::Current());
+
+ pthread_t pthread;
+ int pthread_create_result = pthread_create(&pthread, NULL, RecursiveLockWait::Callback, &state);
+ ASSERT_EQ(0, pthread_create_result);
+
+ state.cv.Wait(Thread::Current());
+
+ state.mu.Unlock(Thread::Current());
+ state.mu.Unlock(Thread::Current());
+ EXPECT_EQ(pthread_join(pthread, NULL), 0);
+}
+
+// This ensures we don't hang when waiting on a recursively locked mutex,
+// which is not supported with bare pthread_mutex_t.
+TEST_F(MutexTest, RecursiveLockWait) {
+ RecursiveLockWaitTest();
+}
+
+TEST_F(MutexTest, SharedLockUnlock) {
+ ReaderWriterMutex mu("test rwmutex");
+ mu.AssertNotHeld(Thread::Current());
+ mu.AssertNotExclusiveHeld(Thread::Current());
+ mu.SharedLock(Thread::Current());
+ mu.AssertSharedHeld(Thread::Current());
+ mu.AssertNotExclusiveHeld(Thread::Current());
+ mu.SharedUnlock(Thread::Current());
+ mu.AssertNotHeld(Thread::Current());
+}
+
+TEST_F(MutexTest, ExclusiveLockUnlock) {
+ ReaderWriterMutex mu("test rwmutex");
+ mu.AssertNotHeld(Thread::Current());
+ mu.ExclusiveLock(Thread::Current());
+ mu.AssertSharedHeld(Thread::Current());
+ mu.AssertExclusiveHeld(Thread::Current());
+ mu.ExclusiveUnlock(Thread::Current());
+ mu.AssertNotHeld(Thread::Current());
+}
+
+// GCC has trouble with our mutex tests, so we have to turn off thread safety analysis.
+static void SharedTryLockUnlockTest() NO_THREAD_SAFETY_ANALYSIS {
+ ReaderWriterMutex mu("test rwmutex");
+ mu.AssertNotHeld(Thread::Current());
+ ASSERT_TRUE(mu.SharedTryLock(Thread::Current()));
+ mu.AssertSharedHeld(Thread::Current());
+ mu.SharedUnlock(Thread::Current());
+ mu.AssertNotHeld(Thread::Current());
+}
+
+TEST_F(MutexTest, SharedTryLockUnlock) {
+ SharedTryLockUnlockTest();
+}
+
+} // namespace art
diff --git a/runtime/base/stl_util.h b/runtime/base/stl_util.h
new file mode 100644
index 0000000..ff9f40c
--- /dev/null
+++ b/runtime/base/stl_util.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_BASE_STL_UTIL_H_
+#define ART_RUNTIME_BASE_STL_UTIL_H_
+
+#include <algorithm>
+#include <sstream>
+
+namespace art {
+
+// Sort and remove duplicates of an STL vector or deque.
+template<class T>
+void STLSortAndRemoveDuplicates(T* v) {
+ std::sort(v->begin(), v->end());
+ v->erase(std::unique(v->begin(), v->end()), v->end());
+}
+
+// STLDeleteContainerPointers()
+// For a range within a container of pointers, calls delete
+// (non-array version) on these pointers.
+// NOTE: for these three functions, we could just implement a DeleteObject
+// functor and then call for_each() on the range and functor, but this
+// requires us to pull in all of algorithm.h, which seems expensive.
+// For hash_[multi]set, it is important that this deletes behind the iterator
+// because the hash_set may call the hash function on the iterator when it is
+// advanced, which could result in the hash function trying to deference a
+// stale pointer.
+template <class ForwardIterator>
+void STLDeleteContainerPointers(ForwardIterator begin,
+ ForwardIterator end) {
+ while (begin != end) {
+ ForwardIterator temp = begin;
+ ++begin;
+ delete *temp;
+ }
+}
+
+// STLDeleteElements() deletes all the elements in an STL container and clears
+// the container. This function is suitable for use with a vector, set,
+// hash_set, or any other STL container which defines sensible begin(), end(),
+// and clear() methods.
+//
+// If container is NULL, this function is a no-op.
+//
+// As an alternative to calling STLDeleteElements() directly, consider
+// ElementDeleter (defined below), which ensures that your container's elements
+// are deleted when the ElementDeleter goes out of scope.
+template <class T>
+void STLDeleteElements(T *container) {
+ if (!container) return;
+ STLDeleteContainerPointers(container->begin(), container->end());
+ container->clear();
+}
+
+// Given an STL container consisting of (key, value) pairs, STLDeleteValues
+// deletes all the "value" components and clears the container. Does nothing
+// in the case it's given a NULL pointer.
+template <class T>
+void STLDeleteValues(T *v) {
+ if (!v) return;
+ for (typename T::iterator i = v->begin(); i != v->end(); ++i) {
+ delete i->second;
+ }
+ v->clear();
+}
+
+template <class T>
+std::string ToString(const T& v) {
+ std::ostringstream os;
+ os << "[";
+ for (size_t i = 0; i < v.size(); ++i) {
+ os << v[i];
+ if (i < v.size() - 1) {
+ os << ", ";
+ }
+ }
+ os << "]";
+ return os.str();
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_BASE_STL_UTIL_H_
diff --git a/runtime/base/stringpiece.cc b/runtime/base/stringpiece.cc
new file mode 100644
index 0000000..47140e3
--- /dev/null
+++ b/runtime/base/stringpiece.cc
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "stringpiece.h"
+
+#include <iostream>
+#include <utility>
+
+namespace art {
+
+void StringPiece::CopyToString(std::string* target) const {
+ target->assign(ptr_, length_);
+}
+
+int StringPiece::copy(char* buf, size_type n, size_type pos) const {
+ int ret = std::min(length_ - pos, n);
+ memcpy(buf, ptr_ + pos, ret);
+ return ret;
+}
+
+StringPiece::size_type StringPiece::find(const StringPiece& s, size_type pos) const {
+ if (length_ < 0 || pos > static_cast<size_type>(length_))
+ return npos;
+
+ const char* result = std::search(ptr_ + pos, ptr_ + length_,
+ s.ptr_, s.ptr_ + s.length_);
+ const size_type xpos = result - ptr_;
+ return xpos + s.length_ <= static_cast<size_type>(length_) ? xpos : npos;
+}
+
+int StringPiece::compare(const StringPiece& x) const {
+ int r = memcmp(ptr_, x.ptr_, std::min(length_, x.length_));
+ if (r == 0) {
+ if (length_ < x.length_) r = -1;
+ else if (length_ > x.length_) r = +1;
+ }
+ return r;
+}
+
+StringPiece::size_type StringPiece::find(char c, size_type pos) const {
+ if (length_ <= 0 || pos >= static_cast<size_type>(length_)) {
+ return npos;
+ }
+ const char* result = std::find(ptr_ + pos, ptr_ + length_, c);
+ return result != ptr_ + length_ ? result - ptr_ : npos;
+}
+
+StringPiece::size_type StringPiece::rfind(const StringPiece& s, size_type pos) const {
+ if (length_ < s.length_) return npos;
+ const size_t ulen = length_;
+ if (s.length_ == 0) return std::min(ulen, pos);
+
+ const char* last = ptr_ + std::min(ulen - s.length_, pos) + s.length_;
+ const char* result = std::find_end(ptr_, last, s.ptr_, s.ptr_ + s.length_);
+ return result != last ? result - ptr_ : npos;
+}
+
+StringPiece::size_type StringPiece::rfind(char c, size_type pos) const {
+ if (length_ <= 0) return npos;
+ for (int i = std::min(pos, static_cast<size_type>(length_ - 1));
+ i >= 0; --i) {
+ if (ptr_[i] == c) {
+ return i;
+ }
+ }
+ return npos;
+}
+
+StringPiece StringPiece::substr(size_type pos, size_type n) const {
+ if (pos > static_cast<size_type>(length_)) pos = length_;
+ if (n > length_ - pos) n = length_ - pos;
+ return StringPiece(ptr_ + pos, n);
+}
+
+const StringPiece::size_type StringPiece::npos = size_type(-1);
+
+std::ostream& operator<<(std::ostream& o, const StringPiece& piece) {
+ o.write(piece.data(), piece.size());
+ return o;
+}
+
+} // namespace art
diff --git a/runtime/base/stringpiece.h b/runtime/base/stringpiece.h
new file mode 100644
index 0000000..91b83f6
--- /dev/null
+++ b/runtime/base/stringpiece.h
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// A string-like object that points to a sized piece of memory.
+//
+// Functions or methods may use const StringPiece& parameters to accept either
+// a "const char*" or a "string" value that will be implicitly converted to
+// a StringPiece. The implicit conversion means that it is often appropriate
+// to include this .h file in other files rather than forward-declaring
+// StringPiece as would be appropriate for most other Google classes.
+//
+// Systematic usage of StringPiece is encouraged as it will reduce unnecessary
+// conversions from "const char*" to "string" and back again.
+
+#ifndef ART_RUNTIME_BASE_STRINGPIECE_H_
+#define ART_RUNTIME_BASE_STRINGPIECE_H_
+
+#include <string.h>
+#include <algorithm>
+#include <cstddef>
+#include <iosfwd>
+#include <string>
+
+namespace art {
+
+class StringPiece {
+ private:
+ const char* ptr_;
+ int length_;
+
+ public:
+ // We provide non-explicit singleton constructors so users can pass
+ // in a "const char*" or a "string" wherever a "StringPiece" is
+ // expected.
+ StringPiece() : ptr_(NULL), length_(0) { }
+ StringPiece(const char* str) // NOLINT
+ : ptr_(str), length_((str == NULL) ? 0 : static_cast<int>(strlen(str))) { }
+ StringPiece(const std::string& str) // NOLINT
+ : ptr_(str.data()), length_(static_cast<int>(str.size())) { }
+ StringPiece(const char* offset, int len) : ptr_(offset), length_(len) { }
+
+ // data() may return a pointer to a buffer with embedded NULs, and the
+ // returned buffer may or may not be null terminated. Therefore it is
+ // typically a mistake to pass data() to a routine that expects a NUL
+ // terminated string.
+ const char* data() const { return ptr_; }
+ int size() const { return length_; }
+ int length() const { return length_; }
+ bool empty() const { return length_ == 0; }
+
+ void clear() {
+ ptr_ = NULL;
+ length_ = 0;
+ }
+ void set(const char* data, int len) {
+ ptr_ = data;
+ length_ = len;
+ }
+ void set(const char* str) {
+ ptr_ = str;
+ if (str != NULL)
+ length_ = static_cast<int>(strlen(str));
+ else
+ length_ = 0;
+ }
+ void set(const void* data, int len) {
+ ptr_ = reinterpret_cast<const char*>(data);
+ length_ = len;
+ }
+
+ char operator[](int i) const { return ptr_[i]; }
+
+ void remove_prefix(int n) {
+ ptr_ += n;
+ length_ -= n;
+ }
+
+ void remove_suffix(int n) {
+ length_ -= n;
+ }
+
+ int compare(const StringPiece& x) const;
+
+ std::string as_string() const {
+ return std::string(data(), size());
+ }
+ // We also define ToString() here, since many other string-like
+ // interfaces name the routine that converts to a C++ string
+ // "ToString", and it's confusing to have the method that does that
+ // for a StringPiece be called "as_string()". We also leave the
+ // "as_string()" method defined here for existing code.
+ std::string ToString() const {
+ return std::string(data(), size());
+ }
+
+ void CopyToString(std::string* target) const;
+ void AppendToString(std::string* target) const;
+
+ // Does "this" start with "x"
+ bool starts_with(const StringPiece& x) const {
+ return ((length_ >= x.length_) &&
+ (memcmp(ptr_, x.ptr_, x.length_) == 0));
+ }
+
+ // Does "this" end with "x"
+ bool ends_with(const StringPiece& x) const {
+ return ((length_ >= x.length_) &&
+ (memcmp(ptr_ + (length_-x.length_), x.ptr_, x.length_) == 0));
+ }
+
+ // standard STL container boilerplate
+ typedef char value_type;
+ typedef const char* pointer;
+ typedef const char& reference;
+ typedef const char& const_reference;
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ static const size_type npos;
+ typedef const char* const_iterator;
+ typedef const char* iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+ iterator begin() const { return ptr_; }
+ iterator end() const { return ptr_ + length_; }
+ const_reverse_iterator rbegin() const {
+ return const_reverse_iterator(ptr_ + length_);
+ }
+ const_reverse_iterator rend() const {
+ return const_reverse_iterator(ptr_);
+ }
+ // STLS says return size_type, but Google says return int
+ int max_size() const { return length_; }
+ int capacity() const { return length_; }
+
+ int copy(char* buf, size_type n, size_type pos = 0) const;
+
+ size_type find(const StringPiece& s, size_type pos = 0) const;
+ size_type find(char c, size_type pos = 0) const;
+ size_type rfind(const StringPiece& s, size_type pos = npos) const;
+ size_type rfind(char c, size_type pos = npos) const;
+
+ StringPiece substr(size_type pos, size_type n = npos) const;
+};
+
+// This large function is defined inline so that in a fairly common case where
+// one of the arguments is a literal, the compiler can elide a lot of the
+// following comparisons.
+inline bool operator==(const StringPiece& x, const StringPiece& y) {
+ int len = x.size();
+ if (len != y.size()) {
+ return false;
+ }
+
+ const char* p1 = x.data();
+ const char* p2 = y.data();
+ if (p1 == p2) {
+ return true;
+ }
+ if (len <= 0) {
+ return true;
+ }
+
+ // Test last byte in case strings share large common prefix
+ if (p1[len-1] != p2[len-1]) return false;
+ if (len == 1) return true;
+
+ // At this point we can, but don't have to, ignore the last byte. We use
+ // this observation to fold the odd-length case into the even-length case.
+ len &= ~1;
+
+ return memcmp(p1, p2, len) == 0;
+}
+
+inline bool operator!=(const StringPiece& x, const StringPiece& y) {
+ return !(x == y);
+}
+
+inline bool operator<(const StringPiece& x, const StringPiece& y) {
+ const int r = memcmp(x.data(), y.data(),
+ std::min(x.size(), y.size()));
+ return ((r < 0) || ((r == 0) && (x.size() < y.size())));
+}
+
+inline bool operator>(const StringPiece& x, const StringPiece& y) {
+ return y < x;
+}
+
+inline bool operator<=(const StringPiece& x, const StringPiece& y) {
+ return !(x > y);
+}
+
+inline bool operator>=(const StringPiece& x, const StringPiece& y) {
+ return !(x < y);
+}
+
+extern std::ostream& operator<<(std::ostream& o, const StringPiece& piece);
+
+} // namespace art
+
+#endif // ART_RUNTIME_BASE_STRINGPIECE_H_
diff --git a/runtime/base/stringprintf.cc b/runtime/base/stringprintf.cc
new file mode 100644
index 0000000..8fd9257
--- /dev/null
+++ b/runtime/base/stringprintf.cc
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "stringprintf.h"
+
+#include <stdio.h>
+
+namespace art {
+
+void StringAppendV(std::string* dst, const char* format, va_list ap) {
+ // First try with a small fixed size buffer
+ char space[1024];
+
+ // It's possible for methods that use a va_list to invalidate
+ // the data in it upon use. The fix is to make a copy
+ // of the structure before using it and use that copy instead.
+ va_list backup_ap;
+ va_copy(backup_ap, ap);
+ int result = vsnprintf(space, sizeof(space), format, backup_ap);
+ va_end(backup_ap);
+
+ if (result < static_cast<int>(sizeof(space))) {
+ if (result >= 0) {
+ // Normal case -- everything fit.
+ dst->append(space, result);
+ return;
+ }
+
+ if (result < 0) {
+ // Just an error.
+ return;
+ }
+ }
+
+ // Increase the buffer size to the size requested by vsnprintf,
+ // plus one for the closing \0.
+ int length = result+1;
+ char* buf = new char[length];
+
+ // Restore the va_list before we use it again
+ va_copy(backup_ap, ap);
+ result = vsnprintf(buf, length, format, backup_ap);
+ va_end(backup_ap);
+
+ if (result >= 0 && result < length) {
+ // It fit
+ dst->append(buf, result);
+ }
+ delete[] buf;
+}
+
+std::string StringPrintf(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ std::string result;
+ StringAppendV(&result, fmt, ap);
+ va_end(ap);
+ return result;
+}
+
+void StringAppendF(std::string* dst, const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ StringAppendV(dst, format, ap);
+ va_end(ap);
+}
+
+} // namespace art
diff --git a/runtime/base/stringprintf.h b/runtime/base/stringprintf.h
new file mode 100644
index 0000000..4767a75
--- /dev/null
+++ b/runtime/base/stringprintf.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_BASE_STRINGPRINTF_H_
+#define ART_RUNTIME_BASE_STRINGPRINTF_H_
+
+#include <stdarg.h>
+#include <string>
+
+namespace art {
+
+// Returns a string corresponding to printf-like formatting of the arguments.
+std::string StringPrintf(const char* fmt, ...)
+ __attribute__((__format__(__printf__, 1, 2)));
+
+// Appends a printf-like formatting of the arguments to 'dst'.
+void StringAppendF(std::string* dst, const char* fmt, ...)
+ __attribute__((__format__(__printf__, 2, 3)));
+
+// Appends a printf-like formatting of the arguments to 'dst'.
+void StringAppendV(std::string* dst, const char* format, va_list ap);
+
+} // namespace art
+
+#endif // ART_RUNTIME_BASE_STRINGPRINTF_H_
diff --git a/runtime/base/timing_logger.cc b/runtime/base/timing_logger.cc
new file mode 100644
index 0000000..11dc542
--- /dev/null
+++ b/runtime/base/timing_logger.cc
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#define ATRACE_TAG ATRACE_TAG_DALVIK
+#include <stdio.h>
+#include <cutils/trace.h>
+
+#include "timing_logger.h"
+
+#include "base/logging.h"
+#include "thread.h"
+#include "base/stl_util.h"
+#include "base/histogram-inl.h"
+
+#include <cmath>
+#include <iomanip>
+
+namespace art {
+
+CumulativeLogger::CumulativeLogger(const std::string& name)
+ : name_(name),
+ lock_name_("CumulativeLoggerLock" + name),
+ lock_(lock_name_.c_str(), kDefaultMutexLevel, true) {
+ Reset();
+}
+
+CumulativeLogger::~CumulativeLogger() {
+ STLDeleteValues(&histograms_);
+}
+
+void CumulativeLogger::SetName(const std::string& name) {
+ name_.assign(name);
+}
+
+void CumulativeLogger::Start() {
+}
+
+void CumulativeLogger::End() {
+ MutexLock mu(Thread::Current(), lock_);
+ iterations_++;
+}
+
+void CumulativeLogger::Reset() {
+ MutexLock mu(Thread::Current(), lock_);
+ iterations_ = 0;
+ STLDeleteValues(&histograms_);
+}
+
+uint64_t CumulativeLogger::GetTotalNs() const {
+ return GetTotalTime() * kAdjust;
+}
+
+uint64_t CumulativeLogger::GetTotalTime() const {
+ MutexLock mu(Thread::Current(), lock_);
+ uint64_t total = 0;
+ for (CumulativeLogger::HistogramsIterator it = histograms_.begin(), end = histograms_.end();
+ it != end; ++it) {
+ total += it->second->Sum();
+ }
+ return total;
+}
+
+void CumulativeLogger::AddLogger(const base::TimingLogger &logger) {
+ MutexLock mu(Thread::Current(), lock_);
+ const base::TimingLogger::SplitTimings& splits = logger.GetSplits();
+ for (base::TimingLogger::SplitTimingsIterator it = splits.begin(), end = splits.end();
+ it != end; ++it) {
+ base::TimingLogger::SplitTiming split = *it;
+ uint64_t split_time = split.first;
+ const char* split_name = split.second;
+ AddPair(split_name, split_time);
+ }
+}
+
+void CumulativeLogger::Dump(std::ostream &os) {
+ MutexLock mu(Thread::Current(), lock_);
+ DumpHistogram(os);
+}
+
+void CumulativeLogger::AddPair(const std::string &label, uint64_t delta_time) {
+ // Convert delta time to microseconds so that we don't overflow our counters.
+ delta_time /= kAdjust;
+
+ if (histograms_.find(label) == histograms_.end()) {
+ // TODO: Shoud this be a defined constant so we we know out of which orifice 16 and 100 were picked?
+ const size_t max_buckets = Runtime::Current()->GetHeap()->IsLowMemoryMode() ? 16 : 100;
+ // TODO: Should this be a defined constant so we know 50 of WTF?
+ histograms_[label] = new Histogram<uint64_t>(label.c_str(), 50, max_buckets);
+ }
+ histograms_[label]->AddValue(delta_time);
+}
+
+void CumulativeLogger::DumpHistogram(std::ostream &os) {
+ os << "Start Dumping histograms for " << iterations_ << " iterations"
+ << " for " << name_ << "\n";
+ for (CumulativeLogger::HistogramsIterator it = histograms_.begin(), end = histograms_.end();
+ it != end; ++it) {
+ Histogram<uint64_t>::CumulativeData cumulative_data;
+ it->second->CreateHistogram(cumulative_data);
+ it->second->PrintConfidenceIntervals(os, 0.99, cumulative_data);
+ // Reset cumulative values to save memory. We don't expect DumpHistogram to be called often, so
+ // it is not performance critical.
+ }
+ os << "Done Dumping histograms \n";
+}
+
+
+namespace base {
+
+TimingLogger::TimingLogger(const char* name, bool precise, bool verbose)
+ : name_(name), precise_(precise), verbose_(verbose), current_split_(NULL) {
+}
+
+void TimingLogger::Reset() {
+ current_split_ = NULL;
+ splits_.clear();
+}
+
+void TimingLogger::StartSplit(const char* new_split_label) {
+ DCHECK(new_split_label != NULL) << "Starting split (" << new_split_label << ") with null label.";
+ TimingLogger::ScopedSplit* explicit_scoped_split = new TimingLogger::ScopedSplit(new_split_label, this);
+ explicit_scoped_split->explicit_ = true;
+}
+
+void TimingLogger::EndSplit() {
+ CHECK(current_split_ != NULL) << "Ending a non-existent split.";
+ DCHECK(current_split_->label_ != NULL);
+ DCHECK(current_split_->explicit_ == true) << "Explicitly ending scoped split: " << current_split_->label_;
+
+ delete current_split_;
+}
+
+// Ends the current split and starts the one given by the label.
+void TimingLogger::NewSplit(const char* new_split_label) {
+ CHECK(current_split_ != NULL) << "Inserting a new split (" << new_split_label
+ << ") into a non-existent split.";
+ DCHECK(new_split_label != NULL) << "New split (" << new_split_label << ") with null label.";
+
+ current_split_->TailInsertSplit(new_split_label);
+}
+
+uint64_t TimingLogger::GetTotalNs() const {
+ uint64_t total_ns = 0;
+ for (base::TimingLogger::SplitTimingsIterator it = splits_.begin(), end = splits_.end();
+ it != end; ++it) {
+ base::TimingLogger::SplitTiming split = *it;
+ total_ns += split.first;
+ }
+ return total_ns;
+}
+
+void TimingLogger::Dump(std::ostream &os) const {
+ uint64_t longest_split = 0;
+ uint64_t total_ns = 0;
+ for (base::TimingLogger::SplitTimingsIterator it = splits_.begin(), end = splits_.end();
+ it != end; ++it) {
+ base::TimingLogger::SplitTiming split = *it;
+ uint64_t split_time = split.first;
+ longest_split = std::max(longest_split, split_time);
+ total_ns += split_time;
+ }
+ // Compute which type of unit we will use for printing the timings.
+ TimeUnit tu = GetAppropriateTimeUnit(longest_split);
+ uint64_t divisor = GetNsToTimeUnitDivisor(tu);
+ // Print formatted splits.
+ for (base::TimingLogger::SplitTimingsIterator it = splits_.begin(), end = splits_.end();
+ it != end; ++it) {
+ base::TimingLogger::SplitTiming split = *it;
+ uint64_t split_time = split.first;
+ if (!precise_ && divisor >= 1000) {
+ // Make the fractional part 0.
+ split_time -= split_time % (divisor / 1000);
+ }
+ os << name_ << ": " << std::setw(8) << FormatDuration(split_time, tu) << " "
+ << split.second << "\n";
+ }
+ os << name_ << ": end, " << NsToMs(total_ns) << " ms\n";
+}
+
+
+TimingLogger::ScopedSplit::ScopedSplit(const char* label, TimingLogger* timing_logger) {
+ DCHECK(label != NULL) << "New scoped split (" << label << ") with null label.";
+ CHECK(timing_logger != NULL) << "New scoped split (" << label << ") without TimingLogger.";
+ timing_logger_ = timing_logger;
+ label_ = label;
+ running_ns_ = 0;
+ explicit_ = false;
+
+ // Stash away the current split and pause it.
+ enclosing_split_ = timing_logger->current_split_;
+ if (enclosing_split_ != NULL) {
+ enclosing_split_->Pause();
+ }
+
+ timing_logger_->current_split_ = this;
+
+ ATRACE_BEGIN(label_);
+
+ start_ns_ = NanoTime();
+ if (timing_logger_->verbose_) {
+ LOG(INFO) << "Begin: " << label_;
+ }
+}
+
+TimingLogger::ScopedSplit::~ScopedSplit() {
+ uint64_t current_time = NanoTime();
+ uint64_t split_time = current_time - start_ns_;
+ running_ns_ += split_time;
+ ATRACE_END();
+
+ if (timing_logger_->verbose_) {
+ LOG(INFO) << "End: " << label_ << " " << PrettyDuration(split_time);
+ }
+
+ // If one or more enclosed explcitly started splits are not terminated we can
+ // either fail or "unwind" the stack of splits in the timing logger to 'this'
+ // (by deleting the intervening scoped splits). This implements the latter.
+ TimingLogger::ScopedSplit* current = timing_logger_->current_split_;
+ while ((current != NULL) && (current != this)) {
+ delete current;
+ current = timing_logger_->current_split_;
+ }
+
+ CHECK(current != NULL) << "Missing scoped split (" << this->label_
+ << ") in timing logger (" << timing_logger_->name_ << ").";
+ CHECK(timing_logger_->current_split_ == this);
+
+ timing_logger_->splits_.push_back(SplitTiming(running_ns_, label_));
+
+ timing_logger_->current_split_ = enclosing_split_;
+ if (enclosing_split_ != NULL) {
+ enclosing_split_->Resume();
+ }
+}
+
+
+void TimingLogger::ScopedSplit::TailInsertSplit(const char* label) {
+ // Sleight of hand here: Rather than embedding a new scoped split, we're updating the current
+ // scoped split in place. Basically, it's one way to make explicit and scoped splits compose
+ // well while maintaining the current semantics of NewSplit. An alternative is to push a new split
+ // since we unwind the stack of scoped splits in the scoped split destructor. However, this implies
+ // that the current split is not ended by NewSplit (which calls TailInsertSplit), which would
+ // be different from what we had before.
+
+ uint64_t current_time = NanoTime();
+ uint64_t split_time = current_time - start_ns_;
+ ATRACE_END();
+ timing_logger_->splits_.push_back(std::pair<uint64_t, const char*>(split_time, label_));
+
+ if (timing_logger_->verbose_) {
+ LOG(INFO) << "End: " << label_ << " " << PrettyDuration(split_time) << "\n"
+ << "Begin: " << label;
+ }
+
+ label_ = label;
+ start_ns_ = current_time;
+ running_ns_ = 0;
+
+ ATRACE_BEGIN(label);
+}
+
+void TimingLogger::ScopedSplit::Pause() {
+ uint64_t current_time = NanoTime();
+ uint64_t split_time = current_time - start_ns_;
+ running_ns_ += split_time;
+ ATRACE_END();
+}
+
+
+void TimingLogger::ScopedSplit::Resume() {
+ uint64_t current_time = NanoTime();
+
+ start_ns_ = current_time;
+ ATRACE_BEGIN(label_);
+}
+
+} // namespace base
+} // namespace art
diff --git a/runtime/base/timing_logger.h b/runtime/base/timing_logger.h
new file mode 100644
index 0000000..07d1ee0
--- /dev/null
+++ b/runtime/base/timing_logger.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_BASE_TIMING_LOGGER_H_
+#define ART_RUNTIME_BASE_TIMING_LOGGER_H_
+
+#include "base/histogram.h"
+#include "base/macros.h"
+#include "base/mutex.h"
+
+#include <string>
+#include <vector>
+#include <map>
+
+namespace art {
+
+namespace base {
+ class TimingLogger;
+} // namespace base
+
+class CumulativeLogger {
+ public:
+ explicit CumulativeLogger(const std::string& name);
+ void prepare_stats();
+ ~CumulativeLogger();
+ void Start();
+ void End();
+ void Reset();
+ void Dump(std::ostream& os) LOCKS_EXCLUDED(lock_);
+ uint64_t GetTotalNs() const;
+ // Allow the name to be modified, particularly when the cumulative logger is a field within a
+ // parent class that is unable to determine the "name" of a sub-class.
+ void SetName(const std::string& name);
+ void AddLogger(const base::TimingLogger& logger) LOCKS_EXCLUDED(lock_);
+
+ private:
+ typedef std::map<std::string, Histogram<uint64_t> *> Histograms;
+ typedef std::map<std::string, Histogram<uint64_t> *>::const_iterator HistogramsIterator;
+
+ void AddPair(const std::string &label, uint64_t delta_time)
+ EXCLUSIVE_LOCKS_REQUIRED(lock_);
+ void DumpHistogram(std::ostream &os) EXCLUSIVE_LOCKS_REQUIRED(lock_);
+ uint64_t GetTotalTime() const;
+ static const uint64_t kAdjust = 1000;
+ Histograms histograms_ GUARDED_BY(lock_);
+ std::string name_;
+ const std::string lock_name_;
+ mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ size_t iterations_ GUARDED_BY(lock_);
+
+ DISALLOW_COPY_AND_ASSIGN(CumulativeLogger);
+};
+
+namespace base {
+
+
+// A timing logger that knows when a split starts for the purposes of logging tools, like systrace.
+class TimingLogger {
+ public:
+ // Splits are nanosecond times and split names.
+ typedef std::pair<uint64_t, const char*> SplitTiming;
+ typedef std::vector<SplitTiming> SplitTimings;
+ typedef std::vector<SplitTiming>::const_iterator SplitTimingsIterator;
+
+ explicit TimingLogger(const char* name, bool precise, bool verbose);
+
+ // Clears current splits and labels.
+ void Reset();
+
+ // Starts a split
+ void StartSplit(const char* new_split_label);
+
+ // Ends the current split and starts the one given by the label.
+ void NewSplit(const char* new_split_label);
+
+ // Ends the current split and records the end time.
+ void EndSplit();
+
+ uint64_t GetTotalNs() const;
+
+ void Dump(std::ostream& os) const;
+
+ // Scoped timing splits that can be nested and composed with the explicit split
+ // starts and ends.
+ class ScopedSplit {
+ public:
+ explicit ScopedSplit(const char* label, TimingLogger* timing_logger);
+
+ ~ScopedSplit();
+
+ friend class TimingLogger;
+
+ private:
+ // Pauses timing of the split, usually due to nesting of another split.
+ void Pause();
+
+ // Resumes timing of the split, usually because a nested split has ended.
+ void Resume();
+
+ // Used by new split to swap splits in place in a ScopedSplit instance.
+ void TailInsertSplit(const char* label);
+
+ // The scoped split immediately enclosing this split. Essentially, we get a
+ // stack of nested splits through this field.
+ ScopedSplit* enclosing_split_;
+
+ // Was this created via TimingLogger's StartSplit?
+ bool explicit_;
+
+ // The split's name.
+ const char* label_;
+
+ // The current split's latest start time. (It may have been paused and restarted.)
+ uint64_t start_ns_;
+
+ // The running time, outside of pauses.
+ uint64_t running_ns_;
+
+ // The timing logger holding this split.
+ TimingLogger* timing_logger_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedSplit);
+ };
+
+ const SplitTimings& GetSplits() const {
+ return splits_;
+ }
+
+ friend class ScopedSplit;
+ protected:
+ // The name of the timing logger.
+ const char* name_;
+
+ // Do we want to print the exactly recorded split (true) or round down to the time unit being
+ // used (false).
+ const bool precise_;
+
+ // Verbose logging.
+ const bool verbose_;
+
+ // The current scoped split is also the 'top' of the stack of splits in progress.
+ ScopedSplit* current_split_;
+
+ // Splits that have ended.
+ SplitTimings splits_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TimingLogger);
+};
+
+} // namespace base
+} // namespace art
+
+#endif // ART_RUNTIME_BASE_TIMING_LOGGER_H_
diff --git a/runtime/base/timing_logger_test.cc b/runtime/base/timing_logger_test.cc
new file mode 100644
index 0000000..8f28e48
--- /dev/null
+++ b/runtime/base/timing_logger_test.cc
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "timing_logger.h"
+
+#include "common_test.h"
+
+namespace art {
+
+class TimingLoggerTest : public CommonTest {};
+
+// TODO: Negative test cases (improper pairing of EndSplit, etc.)
+
+TEST_F(TimingLoggerTest, StartEnd) {
+ const char* split1name = "First Split";
+ base::TimingLogger timings("StartEnd", true, false);
+
+ timings.StartSplit(split1name);
+
+ timings.EndSplit(); // Ends split1.
+
+ const base::TimingLogger::SplitTimings& splits = timings.GetSplits();
+
+ EXPECT_EQ(1U, splits.size());
+ EXPECT_STREQ(splits[0].second, split1name);
+}
+
+
+TEST_F(TimingLoggerTest, StartNewEnd) {
+ const char* split1name = "First Split";
+ const char* split2name = "Second Split";
+ const char* split3name = "Third Split";
+ base::TimingLogger timings("StartNewEnd", true, false);
+
+ timings.StartSplit(split1name);
+
+ timings.NewSplit(split2name); // Ends split1.
+
+ timings.NewSplit(split3name); // Ends split2.
+
+ timings.EndSplit(); // Ends split3.
+
+ const base::TimingLogger::SplitTimings& splits = timings.GetSplits();
+
+ EXPECT_EQ(3U, splits.size());
+ EXPECT_STREQ(splits[0].second, split1name);
+ EXPECT_STREQ(splits[1].second, split2name);
+ EXPECT_STREQ(splits[2].second, split3name);
+}
+
+TEST_F(TimingLoggerTest, StartNewEndNested) {
+ const char* split1name = "First Split";
+ const char* split2name = "Second Split";
+ const char* split3name = "Third Split";
+ const char* split4name = "Fourth Split";
+ const char* split5name = "Fifth Split";
+ base::TimingLogger timings("StartNewEndNested", true, false);
+
+ timings.StartSplit(split1name);
+
+ timings.NewSplit(split2name); // Ends split1.
+
+ timings.StartSplit(split3name);
+
+ timings.StartSplit(split4name);
+
+ timings.NewSplit(split5name); // Ends split4.
+
+ timings.EndSplit(); // Ends split5.
+
+ timings.EndSplit(); // Ends split3.
+
+ timings.EndSplit(); // Ends split2.
+
+ const base::TimingLogger::SplitTimings& splits = timings.GetSplits();
+
+ EXPECT_EQ(5U, splits.size());
+ EXPECT_STREQ(splits[0].second, split1name);
+ EXPECT_STREQ(splits[1].second, split4name);
+ EXPECT_STREQ(splits[2].second, split5name);
+ EXPECT_STREQ(splits[3].second, split3name);
+ EXPECT_STREQ(splits[4].second, split2name);
+}
+
+
+TEST_F(TimingLoggerTest, Scoped) {
+ const char* outersplit = "Outer Split";
+ const char* innersplit1 = "Inner Split 1";
+ const char* innerinnersplit1 = "Inner Inner Split 1";
+ const char* innersplit2 = "Inner Split 2";
+ base::TimingLogger timings("Scoped", true, false);
+
+ {
+ base::TimingLogger::ScopedSplit outer(outersplit, &timings);
+
+ {
+ base::TimingLogger::ScopedSplit inner1(innersplit1, &timings);
+
+ {
+ base::TimingLogger::ScopedSplit innerinner1(innerinnersplit1, &timings);
+ } // Ends innerinnersplit1.
+ } // Ends innersplit1.
+
+ {
+ base::TimingLogger::ScopedSplit inner2(innersplit2, &timings);
+ } // Ends innersplit2.
+ } // Ends outersplit.
+
+ const base::TimingLogger::SplitTimings& splits = timings.GetSplits();
+
+ EXPECT_EQ(4U, splits.size());
+ EXPECT_STREQ(splits[0].second, innerinnersplit1);
+ EXPECT_STREQ(splits[1].second, innersplit1);
+ EXPECT_STREQ(splits[2].second, innersplit2);
+ EXPECT_STREQ(splits[3].second, outersplit);
+}
+
+
+TEST_F(TimingLoggerTest, ScopedAndExplicit) {
+ const char* outersplit = "Outer Split";
+ const char* innersplit = "Inner Split";
+ const char* innerinnersplit1 = "Inner Inner Split 1";
+ const char* innerinnersplit2 = "Inner Inner Split 2";
+ base::TimingLogger timings("Scoped", true, false);
+
+ timings.StartSplit(outersplit);
+
+ {
+ base::TimingLogger::ScopedSplit inner(innersplit, &timings);
+
+ timings.StartSplit(innerinnersplit1);
+
+ timings.NewSplit(innerinnersplit2); // Ends innerinnersplit1.
+ } // Ends innerinnersplit2, then innersplit.
+
+ timings.EndSplit(); // Ends outersplit.
+
+ const base::TimingLogger::SplitTimings& splits = timings.GetSplits();
+
+ EXPECT_EQ(4U, splits.size());
+ EXPECT_STREQ(splits[0].second, innerinnersplit1);
+ EXPECT_STREQ(splits[1].second, innerinnersplit2);
+ EXPECT_STREQ(splits[2].second, innersplit);
+ EXPECT_STREQ(splits[3].second, outersplit);
+}
+
+} // namespace art
diff --git a/runtime/base/unix_file/README b/runtime/base/unix_file/README
new file mode 100644
index 0000000..e9aec22
--- /dev/null
+++ b/runtime/base/unix_file/README
@@ -0,0 +1,15 @@
+A simple C++ wrapper for Unix file I/O.
+
+This is intended to be lightweight and easy to use, similar to Java's
+RandomAccessFile and related classes. The usual C++ idioms of RAII and "you
+don't pay for what you don't use" apply.
+
+In particular, the basic RandomAccessFile interface is kept small and simple so
+it's trivial to add new implementations.
+
+This code will not log, because it can't know whether that's appropriate in
+your application.
+
+This code will, in general, return -errno on failure. If an operation consisted
+of multiple sub-operations, it will return the errno corresponding to the most
+relevant operation.
diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc
new file mode 100644
index 0000000..36f8ba7
--- /dev/null
+++ b/runtime/base/unix_file/fd_file.cc
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/logging.h"
+#include "base/unix_file/fd_file.h"
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace unix_file {
+
+FdFile::FdFile() : fd_(-1), auto_close_(true) {
+}
+
+FdFile::FdFile(int fd) : fd_(fd), auto_close_(true) {
+}
+
+FdFile::FdFile(int fd, const std::string& path) : fd_(fd), file_path_(path), auto_close_(true) {
+ CHECK_NE(0U, path.size());
+}
+
+FdFile::~FdFile() {
+ if (auto_close_ && fd_ != -1) {
+ Close();
+ }
+}
+
+void FdFile::DisableAutoClose() {
+ auto_close_ = false;
+}
+
+bool FdFile::Open(const std::string& path, int flags) {
+ return Open(path, flags, 0640);
+}
+
+bool FdFile::Open(const std::string& path, int flags, mode_t mode) {
+ CHECK_EQ(fd_, -1) << path;
+ fd_ = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode));
+ if (fd_ == -1) {
+ return false;
+ }
+ file_path_ = path;
+ return true;
+}
+
+int FdFile::Close() {
+ int result = TEMP_FAILURE_RETRY(close(fd_));
+ if (result == -1) {
+ return -errno;
+ } else {
+ fd_ = -1;
+ file_path_ = "";
+ return 0;
+ }
+}
+
+int FdFile::Flush() {
+ int rc = TEMP_FAILURE_RETRY(fdatasync(fd_));
+ return (rc == -1) ? -errno : rc;
+}
+
+int64_t FdFile::Read(char* buf, int64_t byte_count, int64_t offset) const {
+ int rc = TEMP_FAILURE_RETRY(pread64(fd_, buf, byte_count, offset));
+ return (rc == -1) ? -errno : rc;
+}
+
+int FdFile::SetLength(int64_t new_length) {
+ int rc = TEMP_FAILURE_RETRY(ftruncate64(fd_, new_length));
+ return (rc == -1) ? -errno : rc;
+}
+
+int64_t FdFile::GetLength() const {
+ struct stat s;
+ int rc = TEMP_FAILURE_RETRY(fstat(fd_, &s));
+ return (rc == -1) ? -errno : s.st_size;
+}
+
+int64_t FdFile::Write(const char* buf, int64_t byte_count, int64_t offset) {
+ int rc = TEMP_FAILURE_RETRY(pwrite64(fd_, buf, byte_count, offset));
+ return (rc == -1) ? -errno : rc;
+}
+
+int FdFile::Fd() const {
+ return fd_;
+}
+
+bool FdFile::IsOpened() const {
+ return fd_ >= 0;
+}
+
+std::string FdFile::GetPath() const {
+ return file_path_;
+}
+
+bool FdFile::ReadFully(void* buffer, int64_t byte_count) {
+ char* ptr = static_cast<char*>(buffer);
+ while (byte_count > 0) {
+ int bytes_read = TEMP_FAILURE_RETRY(read(fd_, ptr, byte_count));
+ if (bytes_read <= 0) {
+ return false;
+ }
+ byte_count -= bytes_read; // Reduce the number of remaining bytes.
+ ptr += bytes_read; // Move the buffer forward.
+ }
+ return true;
+}
+
+bool FdFile::WriteFully(const void* buffer, int64_t byte_count) {
+ const char* ptr = static_cast<const char*>(buffer);
+ while (byte_count > 0) {
+ int bytes_read = TEMP_FAILURE_RETRY(write(fd_, ptr, byte_count));
+ if (bytes_read < 0) {
+ return false;
+ }
+ byte_count -= bytes_read; // Reduce the number of remaining bytes.
+ ptr += bytes_read; // Move the buffer forward.
+ }
+ return true;
+}
+
+} // namespace unix_file
diff --git a/runtime/base/unix_file/fd_file.h b/runtime/base/unix_file/fd_file.h
new file mode 100644
index 0000000..79a0db9
--- /dev/null
+++ b/runtime/base/unix_file/fd_file.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_BASE_UNIX_FILE_FD_FILE_H_
+#define ART_RUNTIME_BASE_UNIX_FILE_FD_FILE_H_
+
+#include <fcntl.h>
+#include <string>
+#include "base/unix_file/random_access_file.h"
+#include "base/macros.h"
+
+namespace unix_file {
+
+// A RandomAccessFile implementation backed by a file descriptor.
+//
+// Not thread safe.
+class FdFile : public RandomAccessFile {
+ public:
+ FdFile();
+ // Creates an FdFile using the given file descriptor. Takes ownership of the
+ // file descriptor. (Use DisableAutoClose to retain ownership.)
+ explicit FdFile(int fd);
+ explicit FdFile(int fd, const std::string& path);
+
+ // Destroys an FdFile, closing the file descriptor if Close hasn't already
+ // been called. (If you care about the return value of Close, call it
+ // yourself; this is meant to handle failure cases and read-only accesses.
+ // Note though that calling Close and checking its return value is still no
+ // guarantee that data actually made it to stable storage.)
+ virtual ~FdFile();
+
+ // Opens file 'file_path' using 'flags' and 'mode'.
+ bool Open(const std::string& file_path, int flags);
+ bool Open(const std::string& file_path, int flags, mode_t mode);
+
+ // RandomAccessFile API.
+ virtual int Close();
+ virtual int64_t Read(char* buf, int64_t byte_count, int64_t offset) const;
+ virtual int SetLength(int64_t new_length);
+ virtual int64_t GetLength() const;
+ virtual int64_t Write(const char* buf, int64_t byte_count, int64_t offset);
+ virtual int Flush();
+
+ // Bonus API.
+ int Fd() const;
+ bool IsOpened() const;
+ std::string GetPath() const;
+ void DisableAutoClose();
+ bool ReadFully(void* buffer, int64_t byte_count);
+ bool WriteFully(const void* buffer, int64_t byte_count);
+
+ private:
+ int fd_;
+ std::string file_path_;
+ bool auto_close_;
+
+ DISALLOW_COPY_AND_ASSIGN(FdFile);
+};
+
+} // namespace unix_file
+
+#endif // ART_RUNTIME_BASE_UNIX_FILE_FD_FILE_H_
diff --git a/runtime/base/unix_file/fd_file_test.cc b/runtime/base/unix_file/fd_file_test.cc
new file mode 100644
index 0000000..d620666
--- /dev/null
+++ b/runtime/base/unix_file/fd_file_test.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/unix_file/fd_file.h"
+#include "base/unix_file/random_access_file_test.h"
+#include "gtest/gtest.h"
+
+namespace unix_file {
+
+class FdFileTest : public RandomAccessFileTest {
+ protected:
+ virtual RandomAccessFile* MakeTestFile() {
+ return new FdFile(fileno(tmpfile()));
+ }
+};
+
+TEST_F(FdFileTest, Read) {
+ TestRead();
+}
+
+TEST_F(FdFileTest, SetLength) {
+ TestSetLength();
+}
+
+TEST_F(FdFileTest, Write) {
+ TestWrite();
+}
+
+TEST_F(FdFileTest, UnopenedFile) {
+ FdFile file;
+ EXPECT_EQ(-1, file.Fd());
+ EXPECT_FALSE(file.IsOpened());
+ EXPECT_TRUE(file.GetPath().empty());
+}
+
+TEST_F(FdFileTest, OpenClose) {
+ std::string good_path(GetTmpPath("some-file.txt"));
+ FdFile file;
+ ASSERT_TRUE(file.Open(good_path, O_CREAT | O_WRONLY));
+ EXPECT_GE(file.Fd(), 0);
+ EXPECT_TRUE(file.IsOpened());
+ EXPECT_EQ(0, file.Close());
+ EXPECT_EQ(-1, file.Fd());
+ EXPECT_FALSE(file.IsOpened());
+ EXPECT_TRUE(file.Open(good_path, O_RDONLY));
+ EXPECT_GE(file.Fd(), 0);
+ EXPECT_TRUE(file.IsOpened());
+}
+
+} // namespace unix_file
diff --git a/runtime/base/unix_file/mapped_file.cc b/runtime/base/unix_file/mapped_file.cc
new file mode 100644
index 0000000..b63fdd3
--- /dev/null
+++ b/runtime/base/unix_file/mapped_file.cc
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/logging.h"
+#include "base/unix_file/mapped_file.h"
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <algorithm>
+#include <string>
+
+namespace unix_file {
+
+MappedFile::~MappedFile() {
+}
+
+int MappedFile::Close() {
+ if (IsMapped()) {
+ Unmap();
+ }
+ return FdFile::Close();
+}
+
+bool MappedFile::MapReadOnly() {
+ CHECK(IsOpened());
+ CHECK(!IsMapped());
+ struct stat st;
+ int result = TEMP_FAILURE_RETRY(fstat(Fd(), &st));
+ if (result == -1) {
+ PLOG(WARNING) << "Failed to stat file '" << GetPath() << "'";
+ return false;
+ }
+ file_size_ = st.st_size;
+ do {
+ mapped_file_ = mmap(NULL, file_size_, PROT_READ, MAP_PRIVATE, Fd(), 0);
+ } while (mapped_file_ == MAP_FAILED && errno == EINTR);
+ if (mapped_file_ == MAP_FAILED) {
+ PLOG(WARNING) << "Failed to mmap file '" << GetPath() << "' of size "
+ << file_size_ << " bytes to memory";
+ return false;
+ }
+ map_mode_ = kMapReadOnly;
+ return true;
+}
+
+bool MappedFile::MapReadWrite(int64_t file_size) {
+ CHECK(IsOpened());
+ CHECK(!IsMapped());
+ int result = TEMP_FAILURE_RETRY(ftruncate64(Fd(), file_size));
+ if (result == -1) {
+ PLOG(ERROR) << "Failed to truncate file '" << GetPath()
+ << "' to size " << file_size;
+ return false;
+ }
+ file_size_ = file_size;
+ do {
+ mapped_file_ =
+ mmap(NULL, file_size_, PROT_READ | PROT_WRITE, MAP_SHARED, Fd(), 0);
+ } while (mapped_file_ == MAP_FAILED && errno == EINTR);
+ if (mapped_file_ == MAP_FAILED) {
+ PLOG(WARNING) << "Failed to mmap file '" << GetPath() << "' of size "
+ << file_size_ << " bytes to memory";
+ return false;
+ }
+ map_mode_ = kMapReadWrite;
+ return true;
+}
+
+bool MappedFile::Unmap() {
+ CHECK(IsMapped());
+ int result = TEMP_FAILURE_RETRY(munmap(mapped_file_, file_size_));
+ if (result == -1) {
+ PLOG(WARNING) << "Failed unmap file '" << GetPath() << "' of size "
+ << file_size_;
+ return false;
+ } else {
+ mapped_file_ = NULL;
+ file_size_ = -1;
+ return true;
+ }
+}
+
+int64_t MappedFile::Read(char* buf, int64_t byte_count, int64_t offset) const {
+ if (IsMapped()) {
+ if (offset < 0) {
+ errno = EINVAL;
+ return -errno;
+ }
+ int64_t read_size = std::max(0LL, std::min(byte_count, file_size_ - offset));
+ if (read_size > 0) {
+ memcpy(buf, data() + offset, read_size);
+ }
+ return read_size;
+ } else {
+ return FdFile::Read(buf, byte_count, offset);
+ }
+}
+
+int MappedFile::SetLength(int64_t new_length) {
+ CHECK(!IsMapped());
+ return FdFile::SetLength(new_length);
+}
+
+int64_t MappedFile::GetLength() const {
+ if (IsMapped()) {
+ return file_size_;
+ } else {
+ return FdFile::GetLength();
+ }
+}
+
+int MappedFile::Flush() {
+ int rc = IsMapped() ? TEMP_FAILURE_RETRY(msync(mapped_file_, file_size_, 0)) : FdFile::Flush();
+ return rc == -1 ? -errno : 0;
+}
+
+int64_t MappedFile::Write(const char* buf, int64_t byte_count, int64_t offset) {
+ if (IsMapped()) {
+ CHECK_EQ(kMapReadWrite, map_mode_);
+ if (offset < 0) {
+ errno = EINVAL;
+ return -errno;
+ }
+ int64_t write_size = std::max(0LL, std::min(byte_count, file_size_ - offset));
+ if (write_size > 0) {
+ memcpy(data() + offset, buf, write_size);
+ }
+ return write_size;
+ } else {
+ return FdFile::Write(buf, byte_count, offset);
+ }
+}
+
+int64_t MappedFile::size() const {
+ return GetLength();
+}
+
+bool MappedFile::IsMapped() const {
+ return mapped_file_ != NULL && mapped_file_ != MAP_FAILED;
+}
+
+char* MappedFile::data() const {
+ CHECK(IsMapped());
+ return static_cast<char*>(mapped_file_);
+}
+
+} // namespace unix_file
diff --git a/runtime/base/unix_file/mapped_file.h b/runtime/base/unix_file/mapped_file.h
new file mode 100644
index 0000000..28cc551
--- /dev/null
+++ b/runtime/base/unix_file/mapped_file.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_BASE_UNIX_FILE_MAPPED_FILE_H_
+#define ART_RUNTIME_BASE_UNIX_FILE_MAPPED_FILE_H_
+
+#include <fcntl.h>
+#include <string>
+#include "base/unix_file/fd_file.h"
+
+namespace unix_file {
+
+// Random access file which handles an mmap(2), munmap(2) pair in C++
+// RAII style. When a file is mmapped, the random access file
+// interface accesses the mmapped memory directly; otherwise, the
+// standard file I/O is used. Whenever a function fails, it returns
+// false and errno is set to the corresponding error code.
+class MappedFile : public FdFile {
+ public:
+ // File modes used in Open().
+ enum FileMode {
+ kReadOnlyMode = O_RDONLY | O_LARGEFILE,
+ kReadWriteMode = O_CREAT | O_RDWR | O_LARGEFILE,
+ };
+
+ MappedFile() : FdFile(), file_size_(-1), mapped_file_(NULL) {
+ }
+ // Creates a MappedFile using the given file descriptor. Takes ownership of
+ // the file descriptor.
+ explicit MappedFile(int fd) : FdFile(fd), file_size_(-1), mapped_file_(NULL) {
+ }
+
+ // Unmaps and closes the file if needed.
+ virtual ~MappedFile();
+
+ // Maps an opened file to memory in the read-only mode.
+ bool MapReadOnly();
+
+ // Maps an opened file to memory in the read-write mode. Before the
+ // file is mapped, it is truncated to 'file_size' bytes.
+ bool MapReadWrite(int64_t file_size);
+
+ // Unmaps a mapped file so that, e.g., SetLength() may be invoked.
+ bool Unmap();
+
+ // RandomAccessFile API.
+ // The functions below require that the file is open, but it doesn't
+ // have to be mapped.
+ virtual int Close();
+ virtual int64_t Read(char* buf, int64_t byte_count, int64_t offset) const;
+ // SetLength() requires that the file is not mmapped.
+ virtual int SetLength(int64_t new_length);
+ virtual int64_t GetLength() const;
+ virtual int Flush();
+ // Write() requires that, if the file is mmapped, it is mmapped in
+ // the read-write mode. Writes past the end of file are discarded.
+ virtual int64_t Write(const char* buf, int64_t byte_count, int64_t offset);
+
+ // A convenience method equivalent to GetLength().
+ int64_t size() const;
+
+ // Returns true if the file has been mmapped.
+ bool IsMapped() const;
+
+ // Returns a pointer to the start of the memory mapping once the
+ // file is successfully mapped; crashes otherwise.
+ char* data() const;
+
+ private:
+ enum MapMode {
+ kMapReadOnly = 1,
+ kMapReadWrite = 2,
+ };
+
+ mutable int64_t file_size_; // May be updated in GetLength().
+ void* mapped_file_;
+ MapMode map_mode_;
+
+ DISALLOW_COPY_AND_ASSIGN(MappedFile);
+};
+
+} // namespace unix_file
+
+#endif // ART_RUNTIME_BASE_UNIX_FILE_MAPPED_FILE_H_
diff --git a/runtime/base/unix_file/mapped_file_test.cc b/runtime/base/unix_file/mapped_file_test.cc
new file mode 100644
index 0000000..3dda02f
--- /dev/null
+++ b/runtime/base/unix_file/mapped_file_test.cc
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/unix_file/mapped_file.h"
+#include "base/logging.h"
+#include "base/unix_file/fd_file.h"
+#include "base/unix_file/random_access_file_test.h"
+#include "base/unix_file/random_access_file_utils.h"
+#include "base/unix_file/string_file.h"
+#include "gtest/gtest.h"
+
+namespace unix_file {
+
+class MappedFileTest : public RandomAccessFileTest {
+ protected:
+ MappedFileTest() : kContent("some content") {
+ }
+
+ void SetUp() {
+ art::CommonTest::SetEnvironmentVariables(android_data_);
+
+ good_path_ = GetTmpPath("some-file.txt");
+ int fd = TEMP_FAILURE_RETRY(open(good_path_.c_str(), O_CREAT|O_RDWR, 0666));
+ FdFile dst(fd);
+
+ StringFile src;
+ src.Assign(kContent);
+
+ ASSERT_TRUE(CopyFile(src, &dst));
+ }
+
+ virtual RandomAccessFile* MakeTestFile() {
+ TEMP_FAILURE_RETRY(truncate(good_path_.c_str(), 0));
+ MappedFile* f = new MappedFile;
+ CHECK(f->Open(good_path_, MappedFile::kReadWriteMode));
+ return f;
+ }
+
+ const std::string kContent;
+ std::string good_path_;
+};
+
+TEST_F(MappedFileTest, OkayToNotUse) {
+ MappedFile file;
+ EXPECT_EQ(-1, file.Fd());
+ EXPECT_FALSE(file.IsOpened());
+ EXPECT_FALSE(file.IsMapped());
+}
+
+TEST_F(MappedFileTest, OpenClose) {
+ MappedFile file;
+ ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadOnlyMode));
+ EXPECT_GE(file.Fd(), 0);
+ EXPECT_TRUE(file.IsOpened());
+ EXPECT_EQ(kContent.size(), file.size());
+ EXPECT_EQ(0, file.Close());
+ EXPECT_EQ(-1, file.Fd());
+ EXPECT_FALSE(file.IsOpened());
+}
+
+TEST_F(MappedFileTest, OpenFdClose) {
+ FILE* f = tmpfile();
+ ASSERT_TRUE(f != NULL);
+ MappedFile file(fileno(f));
+ EXPECT_GE(file.Fd(), 0);
+ EXPECT_TRUE(file.IsOpened());
+ EXPECT_EQ(0, file.Close());
+}
+
+TEST_F(MappedFileTest, CanUseAfterMapReadOnly) {
+ MappedFile file;
+ ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadOnlyMode));
+ EXPECT_FALSE(file.IsMapped());
+ EXPECT_TRUE(file.MapReadOnly());
+ EXPECT_TRUE(file.IsMapped());
+ EXPECT_EQ(kContent.size(), file.size());
+ ASSERT_TRUE(file.data());
+ EXPECT_EQ(0, memcmp(kContent.c_str(), file.data(), file.size()));
+ EXPECT_EQ(0, file.Flush());
+}
+
+TEST_F(MappedFileTest, CanUseAfterMapReadWrite) {
+ MappedFile file;
+ ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadWriteMode));
+ EXPECT_FALSE(file.IsMapped());
+ EXPECT_TRUE(file.MapReadWrite(1));
+ EXPECT_TRUE(file.IsMapped());
+ EXPECT_EQ(1, file.size());
+ ASSERT_TRUE(file.data());
+ EXPECT_EQ(kContent[0], *file.data());
+ EXPECT_EQ(0, file.Flush());
+}
+
+TEST_F(MappedFileTest, CanWriteNewData) {
+ const std::string new_path(GetTmpPath("new-file.txt"));
+ ASSERT_EQ(-1, unlink(new_path.c_str()));
+ ASSERT_EQ(ENOENT, errno);
+
+ MappedFile file;
+ ASSERT_TRUE(file.Open(new_path, MappedFile::kReadWriteMode));
+ EXPECT_TRUE(file.MapReadWrite(kContent.size()));
+ EXPECT_TRUE(file.IsMapped());
+ EXPECT_EQ(kContent.size(), file.size());
+ ASSERT_TRUE(file.data());
+ memcpy(file.data(), kContent.c_str(), kContent.size());
+ EXPECT_EQ(0, file.Close());
+ EXPECT_FALSE(file.IsMapped());
+
+ FdFile new_file(TEMP_FAILURE_RETRY(open(new_path.c_str(), O_RDONLY)));
+ StringFile buffer;
+ ASSERT_TRUE(CopyFile(new_file, &buffer));
+ EXPECT_EQ(kContent, buffer.ToStringPiece());
+ EXPECT_EQ(0, unlink(new_path.c_str()));
+}
+
+TEST_F(MappedFileTest, FileMustExist) {
+ const std::string bad_path(GetTmpPath("does-not-exist.txt"));
+ MappedFile file;
+ EXPECT_FALSE(file.Open(bad_path, MappedFile::kReadOnlyMode));
+ EXPECT_EQ(-1, file.Fd());
+}
+
+TEST_F(MappedFileTest, FileMustBeWritable) {
+ MappedFile file;
+ ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadOnlyMode));
+ EXPECT_FALSE(file.MapReadWrite(10));
+}
+
+TEST_F(MappedFileTest, RemappingAllowedUntilSuccess) {
+ MappedFile file;
+ ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadOnlyMode));
+ EXPECT_FALSE(file.MapReadWrite(10));
+ EXPECT_FALSE(file.MapReadWrite(10));
+}
+
+TEST_F(MappedFileTest, ResizeMappedFile) {
+ MappedFile file;
+ ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadWriteMode));
+ ASSERT_TRUE(file.MapReadWrite(10));
+ EXPECT_EQ(10, file.GetLength());
+ EXPECT_TRUE(file.Unmap());
+ EXPECT_TRUE(file.MapReadWrite(20));
+ EXPECT_EQ(20, file.GetLength());
+ EXPECT_EQ(0, file.Flush());
+ EXPECT_TRUE(file.Unmap());
+ EXPECT_EQ(0, file.Flush());
+ EXPECT_EQ(0, file.SetLength(5));
+ EXPECT_TRUE(file.MapReadOnly());
+ EXPECT_EQ(5, file.GetLength());
+}
+
+TEST_F(MappedFileTest, ReadNotMapped) {
+ TestRead();
+}
+
+TEST_F(MappedFileTest, SetLengthNotMapped) {
+ TestSetLength();
+}
+
+TEST_F(MappedFileTest, WriteNotMapped) {
+ TestWrite();
+}
+
+TEST_F(MappedFileTest, ReadMappedReadOnly) {
+ MappedFile file;
+ ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadOnlyMode));
+ ASSERT_TRUE(file.MapReadOnly());
+ TestReadContent(kContent, &file);
+}
+
+TEST_F(MappedFileTest, ReadMappedReadWrite) {
+ MappedFile file;
+ ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadWriteMode));
+ ASSERT_TRUE(file.MapReadWrite(kContent.size()));
+ TestReadContent(kContent, &file);
+}
+
+TEST_F(MappedFileTest, WriteMappedReadWrite) {
+ TEMP_FAILURE_RETRY(unlink(good_path_.c_str()));
+ MappedFile file;
+ ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadWriteMode));
+ ASSERT_TRUE(file.MapReadWrite(kContent.size()));
+
+ // Can't write to a negative offset.
+ EXPECT_EQ(-EINVAL, file.Write(kContent.c_str(), 0, -123));
+
+ // A zero-length write is a no-op.
+ EXPECT_EQ(0, file.Write(kContent.c_str(), 0, 0));
+ // But the file size is as given when mapped.
+ EXPECT_EQ(kContent.size(), file.GetLength());
+
+ // Data written past the end are discarded.
+ EXPECT_EQ(kContent.size() - 1,
+ file.Write(kContent.c_str(), kContent.size(), 1));
+ EXPECT_EQ(0, memcmp(kContent.c_str(), file.data() + 1, kContent.size() - 1));
+
+ // Data can be overwritten.
+ EXPECT_EQ(kContent.size(), file.Write(kContent.c_str(), kContent.size(), 0));
+ EXPECT_EQ(0, memcmp(kContent.c_str(), file.data(), kContent.size()));
+}
+
+#if 0 // death tests don't work on android yet
+
+class MappedFileDeathTest : public MappedFileTest {};
+
+TEST_F(MappedFileDeathTest, MustMapBeforeUse) {
+ MappedFile file;
+ EXPECT_TRUE(file.Open(good_path_, MappedFile::kReadOnlyMode));
+ EXPECT_DEATH(file.data(), "mapped_");
+}
+
+TEST_F(MappedFileDeathTest, RemappingNotAllowedReadOnly) {
+ MappedFile file;
+ ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadOnlyMode));
+ ASSERT_TRUE(file.MapReadOnly());
+ EXPECT_DEATH(file.MapReadOnly(), "mapped_");
+}
+
+TEST_F(MappedFileDeathTest, RemappingNotAllowedReadWrite) {
+ MappedFile file;
+ ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadWriteMode));
+ ASSERT_TRUE(file.MapReadWrite(10));
+ EXPECT_DEATH(file.MapReadWrite(10), "mapped_");
+}
+
+TEST_F(MappedFileDeathTest, SetLengthMappedReadWrite) {
+ MappedFile file;
+ ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadWriteMode));
+ ASSERT_TRUE(file.MapReadWrite(10));
+ EXPECT_EQ(10, file.GetLength());
+ EXPECT_DEATH(file.SetLength(0), ".*");
+}
+
+TEST_F(MappedFileDeathTest, SetLengthMappedReadOnly) {
+ MappedFile file;
+ ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadOnlyMode));
+ ASSERT_TRUE(file.MapReadOnly());
+ EXPECT_EQ(kContent.size(), file.GetLength());
+ EXPECT_DEATH(file.SetLength(0), ".*");
+}
+
+TEST_F(MappedFileDeathTest, WriteMappedReadOnly) {
+ MappedFile file;
+ ASSERT_TRUE(file.Open(good_path_, MappedFile::kReadOnlyMode));
+ ASSERT_TRUE(file.MapReadOnly());
+ char buf[10];
+ EXPECT_DEATH(file.Write(buf, 0, 0), ".*");
+}
+
+#endif
+
+} // namespace unix_file
diff --git a/runtime/base/unix_file/null_file.cc b/runtime/base/unix_file/null_file.cc
new file mode 100644
index 0000000..050decb
--- /dev/null
+++ b/runtime/base/unix_file/null_file.cc
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/unix_file/null_file.h"
+#include <errno.h>
+
+namespace unix_file {
+
+NullFile::NullFile() {
+}
+
+NullFile::~NullFile() {
+}
+
+int NullFile::Close() {
+ return 0;
+}
+
+int NullFile::Flush() {
+ return 0;
+}
+
+int64_t NullFile::Read(char* buf, int64_t byte_count, int64_t offset) const {
+ if (offset < 0) {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int NullFile::SetLength(int64_t new_length) {
+ if (new_length < 0) {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int64_t NullFile::GetLength() const {
+ return 0;
+}
+
+int64_t NullFile::Write(const char* buf, int64_t byte_count, int64_t offset) {
+ if (offset < 0) {
+ return -EINVAL;
+ }
+ return byte_count;
+}
+
+} // namespace unix_file
diff --git a/runtime/base/unix_file/null_file.h b/runtime/base/unix_file/null_file.h
new file mode 100644
index 0000000..3394731
--- /dev/null
+++ b/runtime/base/unix_file/null_file.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_BASE_UNIX_FILE_NULL_FILE_H_
+#define ART_RUNTIME_BASE_UNIX_FILE_NULL_FILE_H_
+
+#include "base/unix_file/random_access_file.h"
+#include "base/macros.h"
+
+namespace unix_file {
+
+// A RandomAccessFile implementation equivalent to /dev/null. Writes are
+// discarded, and there's no data to be read. Callers could use FdFile in
+// conjunction with /dev/null, but that's not portable and costs a file
+// descriptor. NullFile is "free".
+//
+// Thread safe.
+class NullFile : public RandomAccessFile {
+ public:
+ NullFile();
+ virtual ~NullFile();
+
+ // RandomAccessFile API.
+ virtual int Close();
+ virtual int Flush();
+ virtual int64_t Read(char* buf, int64_t byte_count, int64_t offset) const;
+ virtual int SetLength(int64_t new_length);
+ virtual int64_t GetLength() const;
+ virtual int64_t Write(const char* buf, int64_t byte_count, int64_t offset);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NullFile);
+};
+
+} // namespace unix_file
+
+#endif // ART_RUNTIME_BASE_UNIX_FILE_NULL_FILE_H_
diff --git a/runtime/base/unix_file/null_file_test.cc b/runtime/base/unix_file/null_file_test.cc
new file mode 100644
index 0000000..0f20acd
--- /dev/null
+++ b/runtime/base/unix_file/null_file_test.cc
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/unix_file/null_file.h"
+
+#include <errno.h>
+
+#include "gtest/gtest.h"
+
+namespace unix_file {
+
+class NullFileTest : public testing::Test { };
+
+TEST_F(NullFileTest, Read) {
+ NullFile f;
+ char buf[256];
+ // You can't read a negative number of bytes...
+ ASSERT_EQ(-EINVAL, f.Read(buf, 0, -1));
+ // ...but everything else is fine (though you'll get no data).
+ ASSERT_EQ(0, f.Read(buf, 128, 0));
+ ASSERT_EQ(0, f.Read(buf, 128, 128));
+}
+
+TEST_F(NullFileTest, SetLength) {
+ NullFile f;
+ // You can't set a negative length...
+ ASSERT_EQ(-EINVAL, f.SetLength(-1));
+ // ...but everything else is fine.
+ ASSERT_EQ(0, f.SetLength(0));
+ ASSERT_EQ(0, f.SetLength(128));
+}
+
+TEST_F(NullFileTest, GetLength) {
+ const std::string content("hello");
+ NullFile f;
+ // The length is always 0.
+ ASSERT_EQ(0, f.GetLength());
+ ASSERT_EQ(content.size(), f.Write(content.data(), content.size(), 0));
+ ASSERT_EQ(0, f.GetLength());
+}
+
+TEST_F(NullFileTest, Write) {
+ const std::string content("hello");
+ NullFile f;
+ // You can't write at a negative offset...
+ ASSERT_EQ(-EINVAL, f.Write(content.data(), content.size(), -128));
+ // But you can write anywhere else...
+ ASSERT_EQ(content.size(), f.Write(content.data(), content.size(), 0));
+ ASSERT_EQ(content.size(), f.Write(content.data(), content.size(), 128));
+ // ...though the file will remain empty.
+ ASSERT_EQ(0, f.GetLength());
+}
+
+} // namespace unix_file
diff --git a/runtime/base/unix_file/random_access_file.h b/runtime/base/unix_file/random_access_file.h
new file mode 100644
index 0000000..31a6dbe
--- /dev/null
+++ b/runtime/base/unix_file/random_access_file.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_H_
+#define ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_H_
+
+#include <stdint.h>
+
+namespace unix_file {
+
+// A file interface supporting random-access reading and writing of content,
+// along with the ability to set the length of a file (smaller or greater than
+// its current extent).
+//
+// This interface does not support a stream position (i.e. every read or write
+// must specify an offset). This interface does not imply any buffering policy.
+//
+// All operations return >= 0 on success or -errno on failure.
+//
+// Implementations never return EINTR; callers are spared the need to manually
+// retry interrupted operations.
+//
+// Any concurrent access to files should be externally synchronized.
+class RandomAccessFile {
+ public:
+ virtual ~RandomAccessFile() { }
+
+ virtual int Close() = 0;
+
+ // Reads 'byte_count' bytes into 'buf' starting at offset 'offset' in the
+ // file. Returns the number of bytes actually read.
+ virtual int64_t Read(char* buf, int64_t byte_count, int64_t offset) const = 0;
+
+ // Sets the length of the file to 'new_length'. If this is smaller than the
+ // file's current extent, data is discarded. If this is greater than the
+ // file's current extent, it is as if a write of the relevant number of zero
+ // bytes occurred. Returns 0 on success.
+ virtual int SetLength(int64_t new_length) = 0;
+
+ // Returns the current size of this file.
+ virtual int64_t GetLength() const = 0;
+
+ // Writes 'byte_count' bytes from 'buf' starting at offset 'offset' in the
+ // file. Zero-byte writes are acceptable, and writes past the end are as if
+ // a write of the relevant number of zero bytes also occurred. Returns the
+ // number of bytes actually written.
+ virtual int64_t Write(const char* buf, int64_t byte_count, int64_t offset) = 0;
+
+ // Flushes file data.
+ virtual int Flush() = 0;
+};
+
+} // namespace unix_file
+
+#endif // ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_H_
diff --git a/runtime/base/unix_file/random_access_file_test.h b/runtime/base/unix_file/random_access_file_test.h
new file mode 100644
index 0000000..9d8550d
--- /dev/null
+++ b/runtime/base/unix_file/random_access_file_test.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_TEST_H_
+#define ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_TEST_H_
+
+#include <errno.h>
+
+#include <string>
+
+#include "common_test.h"
+#include "gtest/gtest.h"
+#include "UniquePtr.h"
+
+namespace unix_file {
+
+class RandomAccessFileTest : public testing::Test {
+ protected:
+ virtual ~RandomAccessFileTest() {
+ }
+
+ // Override this to return an instance of the subclass under test that's
+ // backed by a temporary file.
+ virtual RandomAccessFile* MakeTestFile() = 0;
+
+ virtual void SetUp() {
+ art::CommonTest::SetEnvironmentVariables(android_data_);
+ }
+
+ std::string GetTmpPath(const std::string& name) {
+ std::string path;
+ path = android_data_;
+ path += "/";
+ path += name;
+ return path;
+ }
+
+ // TODO(enh): ReadString (and WriteString) might be generally useful.
+ static bool ReadString(RandomAccessFile* f, std::string* s) {
+ s->clear();
+ char buf[256];
+ int64_t n = 0;
+ int64_t offset = 0;
+ while ((n = f->Read(buf, sizeof(buf), offset)) > 0) {
+ s->append(buf, n);
+ offset += n;
+ }
+ return n != -1;
+ }
+
+ void TestRead() {
+ char buf[256];
+ UniquePtr<RandomAccessFile> file(MakeTestFile());
+
+ // Reading from the start of an empty file gets you zero bytes, however many
+ // you ask for.
+ ASSERT_EQ(0, file->Read(buf, 0, 0));
+ ASSERT_EQ(0, file->Read(buf, 123, 0));
+
+ const std::string content("hello");
+ ASSERT_EQ(content.size(), file->Write(content.data(), content.size(), 0));
+
+ TestReadContent(content, file.get());
+ }
+
+ void TestReadContent(const std::string& content, RandomAccessFile* file) {
+ const int buf_size = content.size() + 10;
+ UniquePtr<char> buf(new char[buf_size]);
+ // Can't read from a negative offset.
+ ASSERT_EQ(-EINVAL, file->Read(buf.get(), 0, -123));
+
+ // Reading too much gets us just what's in the file.
+ ASSERT_EQ(content.size(), file->Read(buf.get(), buf_size, 0));
+ ASSERT_EQ(std::string(buf.get(), content.size()), content);
+
+ // We only get as much as we ask for.
+ const size_t short_request = 2;
+ ASSERT_LT(short_request, content.size());
+ ASSERT_EQ(short_request, file->Read(buf.get(), short_request, 0));
+ ASSERT_EQ(std::string(buf.get(), short_request),
+ content.substr(0, short_request));
+
+ // We don't have to start at the beginning.
+ const int non_zero_offset = 2;
+ ASSERT_GT(non_zero_offset, 0);
+ ASSERT_EQ(short_request,
+ file->Read(buf.get(), short_request, non_zero_offset));
+ ASSERT_EQ(std::string(buf.get(), short_request),
+ content.substr(non_zero_offset, short_request));
+
+ // Reading past the end gets us nothing.
+ ASSERT_EQ(0, file->Read(buf.get(), buf_size, file->GetLength()));
+ ASSERT_EQ(0, file->Read(buf.get(), buf_size, file->GetLength() + 1));
+ }
+
+ void TestSetLength() {
+ const std::string content("hello");
+ UniquePtr<RandomAccessFile> file(MakeTestFile());
+ ASSERT_EQ(content.size(), file->Write(content.data(), content.size(), 0));
+ ASSERT_EQ(content.size(), file->GetLength());
+
+ // Can't give a file a negative length.
+ ASSERT_EQ(-EINVAL, file->SetLength(-123));
+
+ // Can truncate the file.
+ int64_t new_length = 2;
+ ASSERT_EQ(0, file->SetLength(new_length));
+ ASSERT_EQ(new_length, file->GetLength());
+ std::string new_content;
+ ASSERT_TRUE(ReadString(file.get(), &new_content));
+ ASSERT_EQ(content.substr(0, 2), new_content);
+
+ // Expanding the file appends zero bytes.
+ new_length = file->GetLength() + 1;
+ ASSERT_EQ(0, file->SetLength(new_length));
+ ASSERT_EQ(new_length, file->GetLength());
+ ASSERT_TRUE(ReadString(file.get(), &new_content));
+ ASSERT_EQ('\0', new_content[new_length - 1]);
+ }
+
+ void TestWrite() {
+ const std::string content("hello");
+ UniquePtr<RandomAccessFile> file(MakeTestFile());
+
+ // Can't write to a negative offset.
+ ASSERT_EQ(-EINVAL, file->Write(content.data(), 0, -123));
+
+ // Writing zero bytes of data is a no-op.
+ ASSERT_EQ(0, file->Write(content.data(), 0, 0));
+ ASSERT_EQ(0, file->GetLength());
+
+ // We can write data.
+ ASSERT_EQ(content.size(), file->Write(content.data(), content.size(), 0));
+ ASSERT_EQ(content.size(), file->GetLength());
+ std::string new_content;
+ ASSERT_TRUE(ReadString(file.get(), &new_content));
+ ASSERT_EQ(new_content, content);
+
+ // We can read it back.
+ char buf[256];
+ ASSERT_EQ(content.size(), file->Read(buf, sizeof(buf), 0));
+ ASSERT_EQ(std::string(buf, content.size()), content);
+
+ // We can append data past the end.
+ ASSERT_EQ(content.size(),
+ file->Write(content.data(), content.size(), file->GetLength() + 1));
+ int64_t new_length = 2*content.size() + 1;
+ ASSERT_EQ(file->GetLength(), new_length);
+ ASSERT_TRUE(ReadString(file.get(), &new_content));
+ ASSERT_EQ(std::string("hello\0hello", new_length), new_content);
+ }
+
+ protected:
+ std::string android_data_;
+};
+
+} // namespace unix_file
+
+#endif // ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_TEST_H_
diff --git a/runtime/base/unix_file/random_access_file_utils.cc b/runtime/base/unix_file/random_access_file_utils.cc
new file mode 100644
index 0000000..df3b308
--- /dev/null
+++ b/runtime/base/unix_file/random_access_file_utils.cc
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vector>
+#include "base/unix_file/random_access_file_utils.h"
+#include "base/unix_file/random_access_file.h"
+
+namespace unix_file {
+
+bool CopyFile(const RandomAccessFile& src, RandomAccessFile* dst) {
+ // We don't call src->GetLength because some files (those in /proc, say)
+ // don't know how long they are. We just read until there's nothing left.
+ std::vector<char> buf(4096);
+ int64_t offset = 0;
+ int64_t n;
+ while ((n = src.Read(&buf[0], buf.size(), offset)) > 0) {
+ if (dst->Write(&buf[0], n, offset) != n) {
+ return false;
+ }
+ offset += n;
+ }
+ return n >= 0;
+}
+
+} // namespace unix_file
diff --git a/runtime/base/unix_file/random_access_file_utils.h b/runtime/base/unix_file/random_access_file_utils.h
new file mode 100644
index 0000000..30c81c0
--- /dev/null
+++ b/runtime/base/unix_file/random_access_file_utils.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_UTILS_H_
+#define ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_UTILS_H_
+
+namespace unix_file {
+
+class RandomAccessFile;
+
+// Copies from 'src' to 'dst'. Reads all the data from 'src', and writes it
+// to 'dst'. Not thread-safe. Neither file will be closed.
+bool CopyFile(const RandomAccessFile& src, RandomAccessFile* dst);
+
+} // namespace unix_file
+
+#endif // ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_UTILS_H_
diff --git a/runtime/base/unix_file/random_access_file_utils_test.cc b/runtime/base/unix_file/random_access_file_utils_test.cc
new file mode 100644
index 0000000..6317922
--- /dev/null
+++ b/runtime/base/unix_file/random_access_file_utils_test.cc
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/unix_file/random_access_file_utils.h"
+#include "base/unix_file/fd_file.h"
+#include "base/unix_file/string_file.h"
+#include "gtest/gtest.h"
+
+namespace unix_file {
+
+class RandomAccessFileUtilsTest : public testing::Test { };
+
+TEST_F(RandomAccessFileUtilsTest, CopyFile) {
+ StringFile src;
+ StringFile dst;
+
+ const std::string content("hello");
+ src.Assign(content);
+ ASSERT_EQ(src.ToStringPiece(), content);
+ ASSERT_EQ(dst.ToStringPiece(), "");
+
+ ASSERT_TRUE(CopyFile(src, &dst));
+ ASSERT_EQ(src.ToStringPiece(), dst.ToStringPiece());
+}
+
+TEST_F(RandomAccessFileUtilsTest, BadSrc) {
+ FdFile src(-1);
+ StringFile dst;
+ ASSERT_FALSE(CopyFile(src, &dst));
+}
+
+TEST_F(RandomAccessFileUtilsTest, BadDst) {
+ StringFile src;
+ FdFile dst(-1);
+
+ // We need some source content to trigger a write.
+ // Copying an empty file is a no-op.
+ src.Assign("hello");
+
+ ASSERT_FALSE(CopyFile(src, &dst));
+}
+
+} // namespace unix_file
diff --git a/runtime/base/unix_file/string_file.cc b/runtime/base/unix_file/string_file.cc
new file mode 100644
index 0000000..ff0d0fa
--- /dev/null
+++ b/runtime/base/unix_file/string_file.cc
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/unix_file/string_file.h"
+#include <errno.h>
+#include <algorithm>
+#include "base/logging.h"
+
+namespace unix_file {
+
+StringFile::StringFile() {
+}
+
+StringFile::~StringFile() {
+}
+
+int StringFile::Close() {
+ return 0;
+}
+
+int StringFile::Flush() {
+ return 0;
+}
+
+int64_t StringFile::Read(char *buf, int64_t byte_count, int64_t offset) const {
+ CHECK(buf);
+ CHECK_GE(byte_count, 0);
+
+ if (offset < 0) {
+ return -EINVAL;
+ }
+
+ const int64_t available_bytes = std::min(byte_count, GetLength() - offset);
+ if (available_bytes < 0) {
+ return 0; // Not an error, but nothing for us to do, either.
+ }
+ memcpy(buf, data_.data() + offset, available_bytes);
+ return available_bytes;
+}
+
+int StringFile::SetLength(int64_t new_length) {
+ if (new_length < 0) {
+ return -EINVAL;
+ }
+ data_.resize(new_length);
+ return 0;
+}
+
+int64_t StringFile::GetLength() const {
+ return data_.size();
+}
+
+int64_t StringFile::Write(const char *buf, int64_t byte_count, int64_t offset) {
+ CHECK(buf);
+ CHECK_GE(byte_count, 0);
+
+ if (offset < 0) {
+ return -EINVAL;
+ }
+
+ if (byte_count == 0) {
+ return 0;
+ }
+
+ // FUSE seems happy to allow writes past the end. (I'd guess it doesn't
+ // synthesize a write of zero bytes so that we're free to implement sparse
+ // files.) GNU as(1) seems to require such writes. Those files are small.
+ const int64_t bytes_past_end = offset - GetLength();
+ if (bytes_past_end > 0) {
+ data_.append(bytes_past_end, '\0');
+ }
+
+ data_.replace(offset, byte_count, buf, byte_count);
+ return byte_count;
+}
+
+void StringFile::Assign(const art::StringPiece &new_data) {
+ data_.assign(new_data.data(), new_data.size());
+}
+
+const art::StringPiece StringFile::ToStringPiece() const {
+ return data_;
+}
+
+} // namespace unix_file
diff --git a/runtime/base/unix_file/string_file.h b/runtime/base/unix_file/string_file.h
new file mode 100644
index 0000000..26904f8
--- /dev/null
+++ b/runtime/base/unix_file/string_file.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_BASE_UNIX_FILE_STRING_FILE_H_
+#define ART_RUNTIME_BASE_UNIX_FILE_STRING_FILE_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/stringpiece.h"
+#include "base/unix_file/random_access_file.h"
+
+namespace unix_file {
+
+// A RandomAccessFile implementation backed by a std::string. (That is, all data is
+// kept in memory.)
+//
+// Not thread safe.
+class StringFile : public RandomAccessFile {
+ public:
+ StringFile();
+ virtual ~StringFile();
+
+ // RandomAccessFile API.
+ virtual int Close();
+ virtual int Flush();
+ virtual int64_t Read(char* buf, int64_t byte_count, int64_t offset) const;
+ virtual int SetLength(int64_t new_length);
+ virtual int64_t GetLength() const;
+ virtual int64_t Write(const char* buf, int64_t byte_count, int64_t offset);
+
+ // Bonus API.
+ void Assign(const art::StringPiece& new_data);
+ const art::StringPiece ToStringPiece() const;
+
+ private:
+ std::string data_;
+
+ DISALLOW_COPY_AND_ASSIGN(StringFile);
+};
+
+} // namespace unix_file
+
+#endif // ART_RUNTIME_BASE_UNIX_FILE_STRING_FILE_H_
diff --git a/runtime/base/unix_file/string_file_test.cc b/runtime/base/unix_file/string_file_test.cc
new file mode 100644
index 0000000..8821461
--- /dev/null
+++ b/runtime/base/unix_file/string_file_test.cc
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/unix_file/string_file.h"
+#include "base/unix_file/random_access_file_test.h"
+#include "gtest/gtest.h"
+
+namespace unix_file {
+
+class StringFileTest : public RandomAccessFileTest {
+ protected:
+ virtual RandomAccessFile* MakeTestFile() {
+ return new StringFile;
+ }
+};
+
+TEST_F(StringFileTest, Read) {
+ TestRead();
+}
+
+TEST_F(StringFileTest, SetLength) {
+ TestSetLength();
+}
+
+TEST_F(StringFileTest, Write) {
+ TestWrite();
+}
+
+} // namespace unix_file