summaryrefslogtreecommitdiffstats
path: root/runtime/base
diff options
context:
space:
mode:
authorBrian Carlstrom <bdc@google.com>2013-07-12 13:46:57 -0700
committerBrian Carlstrom <bdc@google.com>2013-07-12 17:49:01 -0700
commit7940e44f4517de5e2634a7e07d58d0fb26160513 (patch)
treeac90242d96229a6942f6e24ab137bc1f8f2e0025 /runtime/base
parent5cd9e3b122f276f610980cbaf0d2ad6ed4cd9088 (diff)
downloadart-7940e44f4517de5e2634a7e07d58d0fb26160513.zip
art-7940e44f4517de5e2634a7e07d58d0fb26160513.tar.gz
art-7940e44f4517de5e2634a7e07d58d0fb26160513.tar.bz2
Create separate Android.mk for main build targets
The runtime, compiler, dex2oat, and oatdump now are in seperate trees to prevent dependency creep. They can now be individually built without rebuilding the rest of the art projects. dalvikvm and jdwpspy were already this way. Builds in the art directory should behave as before, building everything including tests. Change-Id: Ic6b1151e5ed0f823c3dd301afd2b13eb2d8feb81
Diffstat (limited to 'runtime/base')
-rw-r--r--runtime/base/casts.h93
-rw-r--r--runtime/base/histogram-inl.h255
-rw-r--r--runtime/base/histogram.h120
-rw-r--r--runtime/base/histogram_test.cc268
-rw-r--r--runtime/base/logging.cc235
-rw-r--r--runtime/base/logging.h337
-rw-r--r--runtime/base/logging_android.cc43
-rw-r--r--runtime/base/logging_linux.cc38
-rw-r--r--runtime/base/macros.h201
-rw-r--r--runtime/base/mutex-inl.h187
-rw-r--r--runtime/base/mutex.cc865
-rw-r--r--runtime/base/mutex.h401
-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.h226
-rw-r--r--runtime/base/stringprintf.cc81
-rw-r--r--runtime/base/stringprintf.h38
-rw-r--r--runtime/base/timing_logger.cc269
-rw-r--r--runtime/base/timing_logger.h142
-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
38 files changed, 5717 insertions, 0 deletions
diff --git a/runtime/base/casts.h b/runtime/base/casts.h
new file mode 100644
index 0000000..34c05af
--- /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_SRC_BASE_CASTS_H_
+#define ART_SRC_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_SRC_BASE_CASTS_H_
diff --git a/runtime/base/histogram-inl.h b/runtime/base/histogram-inl.h
new file mode 100644
index 0000000..9514209
--- /dev/null
+++ b/runtime/base/histogram-inl.h
@@ -0,0 +1,255 @@
+/*
+ * 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 SRC_BASE_HISTOGRAM_INL_H_
+#define SRC_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, 0.0);
+ if (value >= max_) {
+ Value new_max = ((value + 1) / bucket_width_ + 1) * bucket_width_;
+ DCHECK_GT(new_max, max_);
+ GrowBuckets(new_max);
+ }
+
+ BucketiseValue(value);
+ new_values_added_ = true;
+}
+
+template <class Value>
+inline Histogram<Value>::Histogram(const std::string name)
+ : kAdjust(1000),
+ kBucketWidth(5),
+ kInitialBucketCount(10),
+ bucket_width_(kBucketWidth),
+ bucket_count_(kInitialBucketCount) {
+ name_ = name;
+ Reset();
+}
+
+template <class Value>
+inline void Histogram<Value>::GrowBuckets(Value new_max) {
+ while (max_ < new_max) {
+ max_ += bucket_width_;
+ ranges_.push_back(max_);
+ frequency_.push_back(0);
+ bucket_count_++;
+ }
+}
+
+template <class Value> inline size_t Histogram<Value>::FindBucket(Value val) {
+ // 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_);
+ size_t bucket_idx = static_cast<size_t>((double)(val - min_) / bucket_width_);
+ DCHECK_GE(bucket_idx, 0ul);
+ DCHECK_LE(bucket_idx, bucket_count_);
+ return bucket_idx;
+}
+
+template <class Value>
+inline void Histogram<Value>::BucketiseValue(Value value) {
+ CHECK_LT(value, max_);
+ sum_ += value;
+ sum_of_squares_ += value * value;
+ size_t bucket_idx = FindBucket(value);
+ sample_size_++;
+ if (value > max_value_added_) {
+ max_value_added_ = value;
+ }
+ if (value < min_value_added_) {
+ min_value_added_ = value;
+ }
+ frequency_[bucket_idx]++;
+}
+
+template <class Value> inline void Histogram<Value>::Initialize() {
+ DCHECK_GT(bucket_count_, 0ul);
+ size_t idx = 0;
+ for (; idx < bucket_count_; idx++) {
+ ranges_.push_back(min_ + static_cast<Value>(idx) * (bucket_width_));
+ frequency_.push_back(0);
+ }
+ // Cumulative frequency and ranges has a length of 1 over frequency.
+ ranges_.push_back(min_ + idx * bucket_width_);
+ max_ = bucket_width_ * bucket_count_;
+}
+
+template <class Value> inline void Histogram<Value>::Reset() {
+ bucket_width_ = kBucketWidth;
+ bucket_count_ = kInitialBucketCount;
+ max_ = bucket_width_ * bucket_count_;
+ 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();
+ new_values_added_ = false;
+ ranges_.clear();
+ frequency_.clear();
+ cumulative_freq_.clear();
+ cumulative_perc_.clear();
+ Initialize();
+}
+
+template <class Value> inline void Histogram<Value>::BuildRanges() {
+ for (size_t idx = 0; idx < bucket_count_; ++idx) {
+ ranges_.push_back(min_ + 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) {
+ DCHECK_GT(sample_size_, 0ull);
+ DCHECK(!new_values_added_);
+ size_t bin_idx = 0;
+ while (bin_idx < cumulative_freq_.size()) {
+ if (bin_idx > 0 &&
+ cumulative_perc_[bin_idx] == cumulative_perc_[bin_idx - 1]) {
+ bin_idx++;
+ continue;
+ }
+ os << ranges_[bin_idx] << ": " << cumulative_freq_[bin_idx] << "\t"
+ << cumulative_perc_[bin_idx] * 100.0 << "%\n";
+ bin_idx++;
+ }
+}
+
+template <class Value>
+inline void Histogram<Value>::PrintConfidenceIntervals(std::ostream &os,
+ double interval) 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) * kAdjust, unit);
+ os << "-" << FormatDuration(Percentile(per_1) * kAdjust, unit) << " ";
+ os << "Avg: " << FormatDuration(Mean() * kAdjust, unit) << " Max: ";
+ os << FormatDuration(Max() * kAdjust, unit) << "\n";
+}
+
+template <class Value> inline void Histogram<Value>::BuildCDF() {
+ DCHECK_EQ(cumulative_freq_.size(), 0ull);
+ DCHECK_EQ(cumulative_perc_.size(), 0ull);
+ uint64_t accumulated = 0;
+
+ cumulative_freq_.push_back(accumulated);
+ cumulative_perc_.push_back(0.0);
+ for (size_t idx = 0; idx < frequency_.size(); idx++) {
+ accumulated += frequency_[idx];
+ cumulative_freq_.push_back(accumulated);
+ cumulative_perc_.push_back(static_cast<double>(accumulated) /
+ static_cast<double>(sample_size_));
+ }
+ DCHECK_EQ(*(cumulative_freq_.end() - 1), sample_size_);
+ DCHECK_EQ(*(cumulative_perc_.end() - 1), 1.0);
+}
+
+template <class Value> inline void Histogram<Value>::CreateHistogram() {
+ DCHECK_GT(sample_size_, 0ull);
+
+ // Create a histogram only if new values are added.
+ if (!new_values_added_)
+ return;
+
+ // Reset cumulative values in case this is not the first time creating histogram.
+ cumulative_freq_.clear();
+ cumulative_perc_.clear();
+ BuildCDF();
+ new_values_added_ = false;
+}
+
+template <class Value>
+inline double Histogram<Value>::Percentile(double per) const {
+ DCHECK_GT(cumulative_perc_.size(), 0ull);
+ size_t idx, upper_idx = 0, lower_idx = 0;
+ for (idx = 0; idx < cumulative_perc_.size(); idx++) {
+
+ if (per <= cumulative_perc_[idx]) {
+ upper_idx = idx;
+ break;
+ }
+
+ if (per >= cumulative_perc_[idx] && idx != 0 &&
+ cumulative_perc_[idx] != cumulative_perc_[idx - 1]) {
+ lower_idx = idx;
+ }
+ }
+
+ double upper_value = static_cast<double>(ranges_[upper_idx]);
+ double lower_value = static_cast<double>(ranges_[lower_idx]);
+
+ double lower_perc = cumulative_perc_[lower_idx];
+ double upper_perc = cumulative_perc_[upper_idx];
+
+ if (per == lower_perc) {
+ return lower_value;
+ }
+ 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 // SRC_BASE_HISTOGRAM_INL_H_
+
diff --git a/runtime/base/histogram.h b/runtime/base/histogram.h
new file mode 100644
index 0000000..6878e71
--- /dev/null
+++ b/runtime/base/histogram.h
@@ -0,0 +1,120 @@
+/*
+ * 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_SRC_BASE_HISTOGRAM_H_
+#define ART_SRC_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 Value kBucketWidth;
+ const size_t kInitialBucketCount;
+
+ public:
+ Histogram(std::string);
+ void AddValue(Value);
+ void CreateHistogram();
+ void Reset();
+ double Mean() const;
+ double Variance() const;
+ double Percentile(double) const;
+ void PrintConfidenceIntervals(std::ostream &, double) const;
+ void PrintBins(std::ostream &);
+
+ 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 BuildRanges();
+ void Initialize();
+ size_t FindBucket(Value);
+ void BucketiseValue(Value);
+ // Builds the cumulative distribution function from the frequency data.
+ // Must be called before using the percentile function.
+ void BuildCDF();
+ // Add more buckets to the histogram to fill in a new value that exceeded
+ // the max_read_value_.
+ void GrowBuckets(Value);
+ bool new_values_added_;
+ std::string name_;
+ // Number of samples placed in histogram.
+ uint64_t sample_size_;
+ // Width of the bucket range. The lower the value is the more accurate
+ // histogram percentiles are.
+ Value bucket_width_;
+ // Number of bucket to have in the histogram. this value will increase
+ // to accommodate for big values that don't fit in initial bucket ranges.
+ size_t bucket_count_;
+ // Represents the ranges of the histograms. Has SampleSize() + 1 elements
+ // e.g. 0,5,10,15 represents ranges 0-5, 5-10, 10-15
+ std::vector<Value> ranges_;
+ // How many occurrences of values fall within a corresponding range that is
+ // saved in the ranges_ vector.
+ std::vector<uint64_t> frequency_;
+ // Accumulative summation of frequencies.
+ // cumulative_freq_[i] = sum(cumulative_freq_[j] : 0 < j < i )
+ std::vector<uint64_t> cumulative_freq_;
+ // Accumulative summation of percentiles; which is the frequency / SampleSize
+ // cumulative_freq_[i] = sum(cumulative_freq_[j] : 0 < j < i )
+ std::vector<double> cumulative_perc_;
+ // Summation of all the elements inputed by the user.
+ Value sum_;
+ // Maximum value that can fit in the histogram, grows adaptively.
+ Value min_;
+ // Minimum value that can fit in the histogram. Fixed to zero for now.
+ 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);
+};
+}
+
+#endif // ART_SRC_BASE_HISTOGRAM_H_
diff --git a/runtime/base/histogram_test.cc b/runtime/base/histogram_test.cc
new file mode 100644
index 0000000..ea3e35f
--- /dev/null
+++ b/runtime/base/histogram_test.cc
@@ -0,0 +1,268 @@
+/*
+ * 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>
+
+using 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"));
+
+ 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"));
+
+ double variance;
+ hist->AddValue(9);
+ hist->AddValue(17);
+ hist->AddValue(28);
+ hist->AddValue(28);
+ hist->CreateHistogram();
+ variance = hist->Variance();
+ EXPECT_EQ(64.25, variance);
+}
+
+TEST(Histtest, Percentile) {
+ UniquePtr<Histogram<uint64_t> > hist(new Histogram<uint64_t>("Percentile"));
+
+ 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();
+ PerValue = hist->Percentile(0.50);
+ EXPECT_EQ(875, static_cast<int>(PerValue * 10));
+}
+
+TEST(Histtest, UpdateRange) {
+ UniquePtr<Histogram<uint64_t> > hist(new Histogram<uint64_t>("UpdateRange"));
+
+ 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();
+ PerValue = hist->Percentile(0.50);
+
+ 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);
+
+ 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"));
+
+ 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();
+ PerValue = hist->Percentile(0.50);
+
+ 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);
+
+ 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"));
+
+ 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();
+ 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();
+ hist->AddValue(175);
+ hist->AddValue(182);
+ hist->AddValue(193);
+ hist->AddValue(200);
+ hist->AddValue(205);
+ hist->AddValue(212);
+ hist->CreateHistogram();
+ PerValue = hist->Percentile(0.50);
+ std::stringstream stream;
+ std::string expected =
+ "MultipleCreateHist:\t99% C.I. 15us-212us Avg: 126.380us Max: 212us\n";
+ hist->PrintConfidenceIntervals(stream, 0.99);
+
+ 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"));
+
+ hist->AddValue(1);
+ hist->CreateHistogram();
+ std::stringstream stream;
+ std::string expected = "SingleValue:\t99% C.I. 1us-1us Avg: 1us Max: 1us\n";
+ hist->PrintConfidenceIntervals(stream, 0.99);
+ EXPECT_EQ(expected, stream.str());
+}
+
+TEST(Histtest, CappingPercentiles) {
+ UniquePtr<Histogram<uint64_t> > hist(new Histogram<uint64_t>("CappingPercentiles"));
+
+ double per_995;
+ double per_005;
+ // All values are similar.
+ for (uint64_t idx = 0ull; idx < 150ull; idx++) {
+ hist->AddValue(0);
+ }
+ hist->CreateHistogram();
+ per_995 = hist->Percentile(0.995);
+ 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();
+ per_005 = hist->Percentile(0.005);
+ per_995 = hist->Percentile(0.995);
+ EXPECT_EQ(1, per_005);
+ EXPECT_EQ(4, per_995);
+}
+
+TEST(Histtest, SpikyValues) {
+ UniquePtr<Histogram<uint64_t> > hist(new Histogram<uint64_t>("SpikyValues"));
+
+ 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();
+ 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);
+ EXPECT_EQ(expected, stream.str());
+}
diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc
new file mode 100644
index 0000000..2c6b6a8
--- /dev/null
+++ b/runtime/base/logging.cc
@@ -0,0 +1,235 @@
+/*
+ * 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();
+ }
+
+ delete data_;
+}
+
+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..8d89e4d
--- /dev/null
+++ b/runtime/base/logging.h
@@ -0,0 +1,337 @@
+/*
+ * 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_SRC_BASE_LOGGING_H_
+#define ART_SRC_BASE_LOGGING_H_
+
+#include <cerrno>
+#include <cstring>
+#include <iostream> // NOLINT
+#include <sstream>
+#include <signal.h>
+#include "base/macros.h"
+#include "log_severity.h"
+
+#define CHECK(x) \
+ if (UNLIKELY(!(x))) \
+ ::art::LogMessage(__FILE__, __LINE__, FATAL, -1).stream() \
+ << "Check failed: " #x << " "
+
+#define CHECK_OP(LHS, RHS, OP) \
+ for (::art::EagerEvaluator<typeof(LHS), typeof(RHS)> _values(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()
+
+//
+// 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*);
+
+// 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*);
+
+ LogMessageData* const 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_;
+
+ // TODO: Remove the #if when Mac OS build server no longer uses GCC 4.2.*.
+#if GCC_VERSION >= 40300
+ DISALLOW_COPY_AND_ASSIGN(HexDump);
+#endif
+};
+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_;
+
+// TODO: Remove the #if when Mac OS build server no longer uses GCC 4.2.*.
+#if GCC_VERSION >= 40300
+ DISALLOW_COPY_AND_ASSIGN(Dumpable);
+#endif
+};
+
+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_;
+
+// TODO: Remove the #if when Mac OS build server no longer uses GCC 4.2.*.
+#if GCC_VERSION >= 40300
+ DISALLOW_COPY_AND_ASSIGN(MutatorLockedDumpable);
+#endif
+};
+
+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 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_SRC_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..847105d
--- /dev/null
+++ b/runtime/base/macros.h
@@ -0,0 +1,201 @@
+/*
+ * 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_SRC_BASE_MACROS_H_
+#define ART_SRC_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
+
+// 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_SRC_BASE_MACROS_H_
diff --git a/runtime/base/mutex-inl.h b/runtime/base/mutex-inl.h
new file mode 100644
index 0000000..f911054
--- /dev/null
+++ b/runtime/base/mutex-inl.h
@@ -0,0 +1,187 @@
+/*
+ * 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_SRC_BASE_MUTEX_INL_H_
+#define ART_SRC_BASE_MUTEX_INL_H_
+
+#include "mutex.h"
+
+#include "cutils/atomic-inline.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:
+#if CONTENTION_LOGGING
+ ScopedContentionRecorder(BaseMutex* mutex, uint64_t blocked_tid, uint64_t owner_tid) :
+ mutex_(mutex), blocked_tid_(blocked_tid), owner_tid_(owner_tid),
+ start_milli_time_(MilliTime()) {
+ }
+#else
+ ScopedContentionRecorder(BaseMutex*, uint64_t, uint64_t) {}
+#endif
+
+ ~ScopedContentionRecorder() {
+#if CONTENTION_LOGGING
+ uint64_t end_milli_time = MilliTime();
+ mutex_->RecordContention(blocked_tid_, owner_tid_, end_milli_time - start_milli_time_);
+#endif
+ }
+
+ private:
+#if CONTENTION_LOGGING
+ BaseMutex* const mutex_;
+ const uint64_t blocked_tid_;
+ const uint64_t owner_tid_;
+ const uint64_t start_milli_time_;
+#endif
+};
+
+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_SRC_BASE_MUTEX_INL_H_
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
new file mode 100644
index 0000000..fbec826
--- /dev/null
+++ b/runtime/base/mutex.cc
@@ -0,0 +1,865 @@
+/*
+ * 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 "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 {
+
+// 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;
+ 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;
+ 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.
+};
+
+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.
+};
+
+#if ART_USE_FUTEXES
+static bool ComputeRelativeTimeSpec(timespec* result_ts, const timespec& lhs, const timespec& rhs) {
+ const long int 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
+
+#if CONTENTION_LOGGING
+// A guard for all_mutexes_ that's not a mutex (Mutexes must CAS to acquire and busy wait).
+static AtomicInteger all_mutexes_guard_;
+// All created mutexes guarded by all_mutexes_guard_.
+std::set<BaseMutex*>* all_mutexes_;
+
+class ScopedAllMutexesLock {
+ public:
+ ScopedAllMutexesLock(const BaseMutex* mutex) : mutex_(mutex) {
+ while (!all_mutexes_guard_.CompareAndSwap(0, reinterpret_cast<int32_t>(mutex))) {
+ NanoSleep(100);
+ }
+ }
+ ~ScopedAllMutexesLock() {
+ while (!all_mutexes_guard_.CompareAndSwap(reinterpret_cast<int32_t>(mutex_), 0)) {
+ NanoSleep(100);
+ }
+ }
+ private:
+ const BaseMutex* const mutex_;
+};
+#endif
+
+BaseMutex::BaseMutex(const char* name, LockLevel level) : level_(level), name_(name) {
+#if CONTENTION_LOGGING
+ ScopedAllMutexesLock mu(this);
+ if (all_mutexes_ == NULL) {
+ // We leak the global set of all mutexes to avoid ordering issues in global variable
+ // construction/destruction.
+ all_mutexes_ = new std::set<BaseMutex*>();
+ }
+ all_mutexes_->insert(this);
+#endif
+}
+
+BaseMutex::~BaseMutex() {
+#if CONTENTION_LOGGING
+ ScopedAllMutexesLock mu(this);
+ all_mutexes_->erase(this);
+#endif
+}
+
+void BaseMutex::DumpAll(std::ostream& os) {
+#if CONTENTION_LOGGING
+ os << "Mutex logging:\n";
+ ScopedAllMutexesLock mu(reinterpret_cast<const BaseMutex*>(-1));
+ typedef std::set<BaseMutex*>::const_iterator It;
+ for (It it = all_mutexes_->begin(); it != all_mutexes_->end(); ++it) {
+ BaseMutex* mutex = *it;
+ mutex->Dump(os);
+ os << "\n";
+ }
+#endif
+}
+
+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);
+ }
+}
+
+void BaseMutex::RecordContention(uint64_t blocked_tid, uint64_t owner_tid, uint64_t milli_time_blocked) {
+#if CONTENTION_LOGGING
+ ++contention_count_;
+ wait_time_ += static_cast<uint32_t>(milli_time_blocked); // May overflow.
+ // This code is intentionally racy as it is only used for diagnostics.
+ uint32_t slot = cur_content_log_entry_;
+ if (contention_log_[slot].blocked_tid == blocked_tid &&
+ contention_log_[slot].owner_tid == blocked_tid) {
+ ++contention_log_[slot].count;
+ } else {
+ uint32_t new_slot;
+ do {
+ slot = cur_content_log_entry_;
+ new_slot = (slot + 1) % kContentionLogSize;
+ } while(!cur_content_log_entry_.CompareAndSwap(slot, new_slot));
+ contention_log_[new_slot].blocked_tid = blocked_tid;
+ contention_log_[new_slot].owner_tid = owner_tid;
+ contention_log_[new_slot].count = 1;
+ }
+#endif
+}
+
+void BaseMutex::DumpContention(std::ostream& os) const {
+#if CONTENTION_LOGGING
+ uint32_t wait_time = wait_time_;
+ uint32_t contention_count = contention_count_;
+ if (contention_count == 0) {
+ os << "never contended";
+ } else {
+ os << "contended " << contention_count << " times, average wait of contender " << (wait_time / contention_count) << "ms";
+ 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 = contention_log_[i].blocked_tid;
+ uint64_t owner_tid = contention_log_[i].owner_tid;
+ uint32_t count = contention_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";
+ }
+ }
+#endif
+}
+
+
+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, GetExclusiveOwnerTid(), SafeGetTid(self));
+ android_atomic_inc(&num_contenders_);
+ if (futex(&state_, FUTEX_WAIT, 1, NULL, NULL, 0) != 0) {
+ if (errno != EAGAIN) {
+ 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
+{
+#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, GetExclusiveOwnerTid(), SafeGetTid(self));
+ android_atomic_inc(&num_pending_writers_);
+ 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_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, GetExclusiveOwnerTid(), SafeGetTid(self));
+ 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) {
+ 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 == EINTR) || (errno == EAGAIN)) {
+ // 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..b627559
--- /dev/null
+++ b/runtime/base/mutex.h
@@ -0,0 +1,401 @@
+/*
+ * 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_SRC_BASE_MUTEX_H_
+#define ART_SRC_BASE_MUTEX_H_
+
+#include <pthread.h>
+#include <stdint.h>
+
+#include <iosfwd>
+#include <string>
+
+#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
+
+// Record Log contention information, dumpable via SIGQUIT.
+#define CONTENTION_LOGGING (0 && ART_USE_FUTEXES)
+const size_t kContentionLogSize = 64;
+#if CONTENTION_LOGGING
+#include "atomic_integer.h"
+#endif
+
+namespace art {
+
+class ScopedContentionRecorder;
+class Thread;
+
+const bool kDebugLocking = kIsDebugBuild;
+
+// 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 milli_time_blocked);
+ void DumpContention(std::ostream& os) const;
+
+ const LockLevel level_; // Support for lock hierarchy.
+ const char* const name_;
+#if CONTENTION_LOGGING
+ // 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;
+ };
+ 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 ms.
+ AtomicInteger wait_time_;
+#endif
+};
+
+// 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;
+ friend class MutexTester;
+ 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
+ friend class MutexTester;
+ 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_SRC_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..eb8be42
--- /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_SRC_BASE_STL_UTIL_H_
+#define ART_SRC_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_SRC_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..3664218
--- /dev/null
+++ b/runtime/base/stringpiece.h
@@ -0,0 +1,226 @@
+/*
+ * 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_SRC_BASE_STRINGPIECE_H_
+#define ART_SRC_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);
+
+struct StringPieceHash {
+ size_t operator()(const StringPiece& string_piece) const {
+ size_t string_size = string_piece.size();
+ const char* string_data = string_piece.data();
+ // This is the java.lang.String hashcode for convenience, not interoperability.
+ size_t hash = 0;
+ while (string_size--) {
+ hash = hash * 31 + *string_data++;
+ }
+ return hash;
+ }
+};
+
+} // namespace art
+
+#endif // ART_SRC_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..d707cc0
--- /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_SRC_BASE_STRINGPRINTF_H_
+#define ART_SRC_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_SRC_BASE_STRINGPRINTF_H_
diff --git a/runtime/base/timing_logger.cc b/runtime/base/timing_logger.cc
new file mode 100644
index 0000000..c7cbbe5
--- /dev/null
+++ b/runtime/base/timing_logger.cc
@@ -0,0 +1,269 @@
+/*
+ * 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 "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 {
+
+void TimingLogger::Reset() {
+ times_.clear();
+ labels_.clear();
+ AddSplit("");
+}
+
+TimingLogger::TimingLogger(const std::string &name, bool precise)
+ : name_(name),
+ precise_(precise) {
+ AddSplit("");
+}
+
+void TimingLogger::AddSplit(const std::string &label) {
+ times_.push_back(NanoTime());
+ labels_.push_back(label);
+}
+
+uint64_t TimingLogger::GetTotalNs() const {
+ return times_.back() - times_.front();
+}
+;
+
+void TimingLogger::Dump(std::ostream &os) const {
+ uint64_t largest_time = 0;
+ os << name_ << ": begin\n";
+ for (size_t i = 1; i < times_.size(); ++i) {
+ uint64_t delta_time = times_[i] - times_[i - 1];
+ largest_time = std::max(largest_time, delta_time);
+ }
+ // Compute which type of unit we will use for printing the timings.
+ TimeUnit tu = GetAppropriateTimeUnit(largest_time);
+ uint64_t divisor = GetNsToTimeUnitDivisor(tu);
+ for (size_t i = 1; i < times_.size(); ++i) {
+ uint64_t delta_time = times_[i] - times_[i - 1];
+ if (!precise_ && divisor >= 1000) {
+ // Make the fraction 0.
+ delta_time -= delta_time % (divisor / 1000);
+ }
+ os << name_ << ": " << std::setw(8) << FormatDuration(delta_time, tu) << " "
+ << labels_[i] << "\n";
+ }
+ os << name_ << ": end, " << NsToMs(GetTotalNs()) << " ms\n";
+}
+
+CumulativeLogger::CumulativeLogger(const std::string& name)
+ : name_(name),
+ lock_name_("CumulativeLoggerLock" + name),
+ lock_(lock_name_.c_str(), kDefaultMutexLevel, true) {
+ Reset();
+}
+
+CumulativeLogger::~CumulativeLogger() {
+ STLDeleteElements(&histograms_);
+}
+
+void CumulativeLogger::SetName(const std::string& name) {
+ name_.assign(name);
+}
+
+void CumulativeLogger::Start() {
+ MutexLock mu(Thread::Current(), lock_);
+ index_ = 0;
+}
+
+void CumulativeLogger::End() {
+ MutexLock mu(Thread::Current(), lock_);
+ iterations_++;
+}
+void CumulativeLogger::Reset() {
+ MutexLock mu(Thread::Current(), lock_);
+ iterations_ = 0;
+ STLDeleteElements(&histograms_);
+}
+
+uint64_t CumulativeLogger::GetTotalNs() const {
+ return GetTotalTime() * kAdjust;
+}
+
+uint64_t CumulativeLogger::GetTotalTime() const {
+ MutexLock mu(Thread::Current(), lock_);
+ uint64_t total = 0;
+ for (size_t i = 0; i < histograms_.size(); ++i) {
+ total += histograms_[i]->Sum();
+ }
+ return total;
+}
+
+void CumulativeLogger::AddLogger(const TimingLogger &logger) {
+ MutexLock mu(Thread::Current(), lock_);
+ DCHECK_EQ(logger.times_.size(), logger.labels_.size());
+ for (size_t i = 1; i < logger.times_.size(); ++i) {
+ const uint64_t delta_time = logger.times_[i] - logger.times_[i - 1];
+ const std::string &label = logger.labels_[i];
+ AddPair(label, delta_time);
+ }
+}
+
+void CumulativeLogger::AddNewLogger(const base::NewTimingLogger &logger) {
+ MutexLock mu(Thread::Current(), lock_);
+ const std::vector<std::pair<uint64_t, const char*> >& splits = logger.GetSplits();
+ typedef std::vector<std::pair<uint64_t, const char*> >::const_iterator It;
+ if (kIsDebugBuild && splits.size() != histograms_.size()) {
+ LOG(ERROR) << "Mismatch in splits.";
+ typedef std::vector<Histogram<uint64_t> *>::const_iterator It2;
+ It it = splits.begin();
+ It2 it2 = histograms_.begin();
+ while ((it != splits.end()) && (it2 != histograms_.end())) {
+ if (it != splits.end()) {
+ LOG(ERROR) << "\tsplit: " << it->second;
+ ++it;
+ }
+ if (it2 != histograms_.end()) {
+ LOG(ERROR) << "\tpreviously record: " << (*it2)->Name();
+ ++it2;
+ }
+ }
+ }
+ for (It it = splits.begin(), end = splits.end(); it != end; ++it) {
+ std::pair<uint64_t, const char*> 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 (index_ >= histograms_.size()) {
+ Histogram<uint64_t> *tmp_hist = new Histogram<uint64_t>(label);
+ tmp_hist->AddValue(delta_time);
+ histograms_.push_back(tmp_hist);
+ } else {
+ histograms_[index_]->AddValue(delta_time);
+ DCHECK_EQ(label, histograms_[index_]->Name());
+ }
+ index_++;
+}
+
+void CumulativeLogger::DumpHistogram(std::ostream &os) {
+ os << "Start Dumping histograms for " << iterations_ << " iterations"
+ << " for " << name_ << "\n";
+ for (size_t Idx = 0; Idx < histograms_.size(); Idx++) {
+ Histogram<uint64_t> &hist = *(histograms_[Idx]);
+ hist.CreateHistogram();
+ hist.PrintConfidenceIntervals(os, 0.99);
+ }
+ os << "Done Dumping histograms \n";
+}
+
+
+namespace base {
+
+NewTimingLogger::NewTimingLogger(const char* name, bool precise, bool verbose)
+ : name_(name), precise_(precise), verbose_(verbose),
+ current_split_(NULL), current_split_start_ns_(0) {
+}
+
+void NewTimingLogger::Reset() {
+ current_split_ = NULL;
+ current_split_start_ns_ = 0;
+ splits_.clear();
+}
+
+void NewTimingLogger::StartSplit(const char* new_split_label) {
+ DCHECK(current_split_ == NULL);
+ if (verbose_) {
+ LOG(INFO) << "Begin: " << new_split_label;
+ }
+ current_split_ = new_split_label;
+ current_split_start_ns_ = NanoTime();
+}
+
+// Ends the current split and starts the one given by the label.
+void NewTimingLogger::NewSplit(const char* new_split_label) {
+ DCHECK(current_split_ != NULL);
+ uint64_t current_time = NanoTime();
+ uint64_t split_time = current_time - current_split_start_ns_;
+ splits_.push_back(std::pair<uint64_t, const char*>(split_time, current_split_));
+ if (verbose_) {
+ LOG(INFO) << "End: " << current_split_ << " " << PrettyDuration(split_time) << "\n"
+ << "Begin: " << new_split_label;
+ }
+ current_split_ = new_split_label;
+ current_split_start_ns_ = current_time;
+}
+
+void NewTimingLogger::EndSplit() {
+ DCHECK(current_split_ != NULL);
+ uint64_t current_time = NanoTime();
+ uint64_t split_time = current_time - current_split_start_ns_;
+ if (verbose_) {
+ LOG(INFO) << "End: " << current_split_ << " " << PrettyDuration(split_time);
+ }
+ splits_.push_back(std::pair<uint64_t, const char*>(split_time, current_split_));
+}
+
+uint64_t NewTimingLogger::GetTotalNs() const {
+ uint64_t total_ns = 0;
+ typedef std::vector<std::pair<uint64_t, const char*> >::const_iterator It;
+ for (It it = splits_.begin(), end = splits_.end(); it != end; ++it) {
+ std::pair<uint64_t, const char*> split = *it;
+ total_ns += split.first;
+ }
+ return total_ns;
+}
+
+void NewTimingLogger::Dump(std::ostream &os) const {
+ uint64_t longest_split = 0;
+ uint64_t total_ns = 0;
+ typedef std::vector<std::pair<uint64_t, const char*> >::const_iterator It;
+ for (It it = splits_.begin(), end = splits_.end(); it != end; ++it) {
+ std::pair<uint64_t, const char*> 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 (It it = splits_.begin(), end = splits_.end(); it != end; ++it) {
+ std::pair<uint64_t, const char*> 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";
+}
+
+} // namespace base
+} // namespace art
diff --git a/runtime/base/timing_logger.h b/runtime/base/timing_logger.h
new file mode 100644
index 0000000..65732b1
--- /dev/null
+++ b/runtime/base/timing_logger.h
@@ -0,0 +1,142 @@
+/*
+ * 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_SRC_TIMING_LOGGER_H_
+#define ART_SRC_TIMING_LOGGER_H_
+
+#include "base/histogram.h"
+#include "base/macros.h"
+#include "base/mutex.h"
+
+#include <string>
+#include <vector>
+
+namespace art {
+
+class CumulativeLogger;
+
+class TimingLogger {
+ public:
+ explicit TimingLogger(const std::string& name, bool precise);
+ void AddSplit(const std::string& label);
+ void Dump(std::ostream& os) const;
+ void Reset();
+ uint64_t GetTotalNs() const;
+
+ protected:
+ const std::string name_;
+ const bool precise_;
+ std::vector<uint64_t> times_;
+ std::vector<std::string> labels_;
+
+ friend class CumulativeLogger;
+};
+
+namespace base {
+ class NewTimingLogger;
+} // 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 TimingLogger& logger) LOCKS_EXCLUDED(lock_);
+ void AddNewLogger(const base::NewTimingLogger& logger) LOCKS_EXCLUDED(lock_);
+
+ private:
+
+ 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;
+ std::vector<Histogram<uint64_t> *> histograms_ GUARDED_BY(lock_);
+ std::string name_;
+ const std::string lock_name_;
+ mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ size_t index_ GUARDED_BY(lock_);
+ size_t iterations_ GUARDED_BY(lock_);
+
+ DISALLOW_COPY_AND_ASSIGN(CumulativeLogger);
+};
+
+namespace base {
+
+// A replacement to timing logger that know when a split starts for the purposes of logging.
+// TODO: replace uses of TimingLogger with base::NewTimingLogger.
+class NewTimingLogger {
+ public:
+ explicit NewTimingLogger(const char* name, bool precise, bool verbose);
+
+ // Clears current splits and labels.
+ void Reset();
+
+ // Starts a split, a split shouldn't be in progress.
+ 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;
+
+ const std::vector<std::pair<uint64_t, const char*> >& GetSplits() const {
+ return splits_;
+ }
+
+ protected:
+ // The name of the timing logger.
+ const std::string 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 name of the current split.
+ const char* current_split_;
+
+ // The nanosecond time the current split started on.
+ uint64_t current_split_start_ns_;
+
+ // Splits are nanosecond times and split names.
+ std::vector<std::pair<uint64_t, const char*> > splits_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NewTimingLogger);
+};
+
+} // namespace base
+} // namespace art
+
+#endif // ART_SRC_TIMING_LOGGER_H_
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..2b33961
--- /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 BASE_UNIX_FILE_FD_FILE_H_
+#define 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 // 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..161100b
--- /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 BASE_UNIX_FILE_MAPPED_FILE_H_
+#define 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 // 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..a3b097d
--- /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..e716603
--- /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 BASE_UNIX_FILE_NULL_FILE_H_
+#define 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 // 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..22da37f
--- /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 BASE_UNIX_FILE_RANDOM_ACCESS_FILE_H_
+#define 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 // 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..3baaeae
--- /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 BASE_UNIX_FILE_RANDOM_ACCESS_FILE_TEST_H_
+#define 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 // 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..0535ead
--- /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 BASE_UNIX_FILE_RANDOM_ACCESS_FILE_UTILS_H_
+#define 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 // 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..8944373
--- /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 BASE_UNIX_FILE_STRING_FILE_H_
+#define 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 // 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