mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 14:06:33 +00:00
ios: Add support for intermediate dump reader and writer.
Due to the limitations of in-process handling, an intermediate dump file is written during exceptions. The data is streamed to a file using only in-process safe methods. The file format is similar to binary JSON, supporting keyed properties, maps and arrays. - Property [key:int, length:int, value:intarray] - StartMap [key:int], followed by repeating Properties until EndMap - StartArray [key:int], followed by repeating Maps until EndArray - EndMap, EndArray, EndDocument Similar to JSON, maps can contain other maps, arrays and properties. Once loaded, the binary file is read into a set of data structures that expose the data, maps and arrays. Bug: crashpad: 31 Change-Id: I43a19204935303afd753c8c7090c54099634ccd6 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/2870807 Reviewed-by: Mark Mentovai <mark@chromium.org> Commit-Queue: Justin Cohen <justincohen@chromium.org>
This commit is contained in:
parent
6f8dfc7730
commit
b2b65a91cf
@ -382,6 +382,19 @@ crashpad_static_library("util") {
|
|||||||
sources += [
|
sources += [
|
||||||
"ios/exception_processor.h",
|
"ios/exception_processor.h",
|
||||||
"ios/exception_processor.mm",
|
"ios/exception_processor.mm",
|
||||||
|
"ios/ios_intermediate_dump_data.cc",
|
||||||
|
"ios/ios_intermediate_dump_data.h",
|
||||||
|
"ios/ios_intermediate_dump_format.h",
|
||||||
|
"ios/ios_intermediate_dump_list.cc",
|
||||||
|
"ios/ios_intermediate_dump_list.h",
|
||||||
|
"ios/ios_intermediate_dump_map.cc",
|
||||||
|
"ios/ios_intermediate_dump_map.h",
|
||||||
|
"ios/ios_intermediate_dump_object.cc",
|
||||||
|
"ios/ios_intermediate_dump_object.h",
|
||||||
|
"ios/ios_intermediate_dump_reader.cc",
|
||||||
|
"ios/ios_intermediate_dump_reader.h",
|
||||||
|
"ios/ios_intermediate_dump_writer.cc",
|
||||||
|
"ios/ios_intermediate_dump_writer.h",
|
||||||
"ios/ios_system_data_collector.h",
|
"ios/ios_system_data_collector.h",
|
||||||
"ios/ios_system_data_collector.mm",
|
"ios/ios_system_data_collector.mm",
|
||||||
"ios/raw_logging.cc",
|
"ios/raw_logging.cc",
|
||||||
@ -793,6 +806,8 @@ source_set("util_test") {
|
|||||||
if (crashpad_is_ios) {
|
if (crashpad_is_ios) {
|
||||||
sources += [
|
sources += [
|
||||||
"ios/exception_processor_test.mm",
|
"ios/exception_processor_test.mm",
|
||||||
|
"ios/ios_intermediate_dump_reader_test.cc",
|
||||||
|
"ios/ios_intermediate_dump_writer_test.cc",
|
||||||
"ios/scoped_vm_read_test.cc",
|
"ios/scoped_vm_read_test.cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
42
util/ios/ios_intermediate_dump_data.cc
Normal file
42
util/ios/ios_intermediate_dump_data.cc
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright 2021 The Crashpad Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// 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 "util/ios/ios_intermediate_dump_data.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
IOSIntermediateDumpData::IOSIntermediateDumpData() : data_() {}
|
||||||
|
|
||||||
|
IOSIntermediateDumpData::~IOSIntermediateDumpData() {}
|
||||||
|
|
||||||
|
IOSIntermediateDumpObject::Type IOSIntermediateDumpData::GetType() const {
|
||||||
|
return Type::kData;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string IOSIntermediateDumpData::GetString() const {
|
||||||
|
return std::string(reinterpret_cast<const char*>(data_.data()), data_.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IOSIntermediateDumpData::GetValueInternal(void* value,
|
||||||
|
size_t value_size) const {
|
||||||
|
if (value_size == data_.size()) {
|
||||||
|
memcpy(value, data_.data(), data_.size());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace crashpad
|
68
util/ios/ios_intermediate_dump_data.h
Normal file
68
util/ios/ios_intermediate_dump_data.h
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
// Copyright 2021 The Crashpad Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// 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 CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_DATA_H_
|
||||||
|
#define CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_DATA_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "base/macros.h"
|
||||||
|
#include "util/ios/ios_intermediate_dump_object.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
//! \brief A data object, consisting of a std::vector<uint8_t>.
|
||||||
|
class IOSIntermediateDumpData : public IOSIntermediateDumpObject {
|
||||||
|
public:
|
||||||
|
IOSIntermediateDumpData();
|
||||||
|
~IOSIntermediateDumpData() override;
|
||||||
|
|
||||||
|
//! \brief Constructs a new data object which owns a std::vector<uint8_t>.
|
||||||
|
//!
|
||||||
|
//! \param[in] data An array of uint8_t.
|
||||||
|
//! \param[in] length The length of \a data.
|
||||||
|
IOSIntermediateDumpData(std::vector<uint8_t> data) : data_(std::move(data)) {}
|
||||||
|
|
||||||
|
// IOSIntermediateDumpObject:
|
||||||
|
Type GetType() const override;
|
||||||
|
|
||||||
|
//! \brief Returns data as a string.
|
||||||
|
std::string GetString() const;
|
||||||
|
|
||||||
|
//! \brief Copies the data into \a value if sizeof(T) matches data_.size().
|
||||||
|
//!
|
||||||
|
//! \param[out] value The data to populate.
|
||||||
|
//!
|
||||||
|
//! \return On success, returns `true`, otherwise returns `false`.
|
||||||
|
template <typename T>
|
||||||
|
bool GetValue(T* value) const {
|
||||||
|
return GetValueInternal(reinterpret_cast<void*>(value), sizeof(*value));
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<uint8_t>& bytes() const { return data_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool GetValueInternal(void* value, size_t value_size) const;
|
||||||
|
|
||||||
|
std::vector<uint8_t> data_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(IOSIntermediateDumpData);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace crashpad
|
||||||
|
|
||||||
|
#endif // CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_DATA_H_
|
120
util/ios/ios_intermediate_dump_format.h
Normal file
120
util/ios/ios_intermediate_dump_format.h
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
// Copyright 2021 The Crashpad Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// 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 CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_FORMAT_H_
|
||||||
|
#define CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_FORMAT_H_
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// Define values for intermediate dump enum class IntermediateDumpKey. Use
|
||||||
|
// |INTERMEDIATE_DUMP_KEYS| so it is easier to print human readable keys in
|
||||||
|
// logs.
|
||||||
|
// clang-format off
|
||||||
|
#define INTERMEDIATE_DUMP_KEYS(TD) \
|
||||||
|
TD(kInvalid, 0) \
|
||||||
|
TD(kVersion, 1) \
|
||||||
|
TD(kMachException, 1000) \
|
||||||
|
TD(kCode, 1001) \
|
||||||
|
TD(kCodeCount, 1002) \
|
||||||
|
TD(kException, 1003) \
|
||||||
|
TD(kFlavor, 1004) \
|
||||||
|
TD(kState, 1005) \
|
||||||
|
TD(kStateCount, 1006) \
|
||||||
|
TD(kSignalException, 2000) \
|
||||||
|
TD(kSignalNumber, 2001) \
|
||||||
|
TD(kSignalCode, 2002) \
|
||||||
|
TD(kSignalAddress, 2003) \
|
||||||
|
TD(kNSException, 2500) \
|
||||||
|
TD(kModules, 3000) \
|
||||||
|
TD(kAddress, 3001) \
|
||||||
|
TD(kFileType, 3002) \
|
||||||
|
TD(kName, 3003) \
|
||||||
|
TD(kSize, 3004) \
|
||||||
|
TD(kDylibCurrentVersion, 3005) \
|
||||||
|
TD(kSourceVersion, 3006) \
|
||||||
|
TD(kTimestamp, 3007) \
|
||||||
|
TD(kUUID, 3008) \
|
||||||
|
TD(kAnnotationObjects, 3009) \
|
||||||
|
TD(kAnnotationsSimpleMap, 3010) \
|
||||||
|
TD(kAnnotationsVector, 3011) \
|
||||||
|
TD(kAnnotationType, 3012) \
|
||||||
|
TD(kAnnotationName, 3013) \
|
||||||
|
TD(kAnnotationValue, 3014) \
|
||||||
|
TD(kAnnotationsCrashInfo, 3015) \
|
||||||
|
TD(kAnnotationsCrashInfoMessage1, 3016) \
|
||||||
|
TD(kAnnotationsCrashInfoMessage2, 3017) \
|
||||||
|
TD(kAnnotationsDyldErrorString, 3018) \
|
||||||
|
TD(kProcessInfo, 4000) \
|
||||||
|
TD(kParentPID, 4001) \
|
||||||
|
TD(kPID, 4002) \
|
||||||
|
TD(kStartTime, 4003) \
|
||||||
|
TD(kSnapshotTime, 4004) \
|
||||||
|
TD(kTaskBasicInfo, 4005) \
|
||||||
|
TD(kTaskThreadTimes, 4006) \
|
||||||
|
TD(kSystemTime, 4007) \
|
||||||
|
TD(kUserTime, 4008) \
|
||||||
|
TD(kSystemInfo, 5000) \
|
||||||
|
TD(kCpuCount, 5001) \
|
||||||
|
TD(kCpuVendor, 5002) \
|
||||||
|
TD(kDaylightName, 5003) \
|
||||||
|
TD(kDaylightOffsetSeconds, 5004) \
|
||||||
|
TD(kHasDaylightSavingTime, 5005) \
|
||||||
|
TD(kIsDaylightSavingTime, 5006) \
|
||||||
|
TD(kMachineDescription, 5007) \
|
||||||
|
TD(kOSVersionBugfix, 5008) \
|
||||||
|
TD(kOSVersionBuild, 5009) \
|
||||||
|
TD(kOSVersionMajor, 5010) \
|
||||||
|
TD(kOSVersionMinor, 5011) \
|
||||||
|
TD(kPageSize, 5012) \
|
||||||
|
TD(kStandardName, 5013) \
|
||||||
|
TD(kStandardOffsetSeconds, 5014) \
|
||||||
|
TD(kVMStat, 5015) \
|
||||||
|
TD(kActive, 5016) \
|
||||||
|
TD(kFree, 5017) \
|
||||||
|
TD(kInactive, 5018) \
|
||||||
|
TD(kWired, 5019) \
|
||||||
|
TD(kThreads, 6000) \
|
||||||
|
TD(kDebugState, 6001) \
|
||||||
|
TD(kFloatState, 6002) \
|
||||||
|
TD(kThreadState, 6003) \
|
||||||
|
TD(kPriority, 6004) \
|
||||||
|
TD(kStackRegionAddress, 6005) \
|
||||||
|
TD(kStackRegionData, 6006) \
|
||||||
|
TD(kSuspendCount, 6007) \
|
||||||
|
TD(kThreadID, 6008) \
|
||||||
|
TD(kThreadDataAddress, 6009) \
|
||||||
|
TD(kThreadUncaughtNSExceptionFrames, 6010) \
|
||||||
|
TD(kThreadContextMemoryRegions, 6011) \
|
||||||
|
TD(kThreadContextMemoryRegionAddress, 6012) \
|
||||||
|
TD(kThreadContextMemoryRegionData, 6013) \
|
||||||
|
TD(kMaxValue, 65535) \
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
|
||||||
|
//! \brief They key for items in the intermediate dump file.
|
||||||
|
//!
|
||||||
|
//! These values are persisted to the intermediate crash dump file. Entries
|
||||||
|
//! should not be renumbered and numeric values should never be reused.
|
||||||
|
enum class IntermediateDumpKey : uint16_t {
|
||||||
|
#define X(NAME, VALUE) NAME = VALUE,
|
||||||
|
INTERMEDIATE_DUMP_KEYS(X)
|
||||||
|
#undef X
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace crashpad
|
||||||
|
|
||||||
|
#endif // CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_FORMAT_H_
|
29
util/ios/ios_intermediate_dump_list.cc
Normal file
29
util/ios/ios_intermediate_dump_list.cc
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2021 The Crashpad Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// 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 "util/ios/ios_intermediate_dump_list.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
IOSIntermediateDumpList::IOSIntermediateDumpList() : list_() {}
|
||||||
|
|
||||||
|
IOSIntermediateDumpList::~IOSIntermediateDumpList() {}
|
||||||
|
|
||||||
|
IOSIntermediateDumpObject::Type IOSIntermediateDumpList::GetType() const {
|
||||||
|
return Type::kList;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace crashpad
|
55
util/ios/ios_intermediate_dump_list.h
Normal file
55
util/ios/ios_intermediate_dump_list.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// Copyright 2021 The Crashpad Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// 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 CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_LIST_H_
|
||||||
|
#define CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_LIST_H_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "base/macros.h"
|
||||||
|
#include "util/ios/ios_intermediate_dump_map.h"
|
||||||
|
#include "util/ios/ios_intermediate_dump_object.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
//! \brief A list object, consisting of a vector of IOSIntermediateDumpMap.
|
||||||
|
//!
|
||||||
|
//! Provides a wrapper around an internal std::vector.
|
||||||
|
class IOSIntermediateDumpList : public IOSIntermediateDumpObject {
|
||||||
|
public:
|
||||||
|
IOSIntermediateDumpList();
|
||||||
|
~IOSIntermediateDumpList() override;
|
||||||
|
|
||||||
|
// IOSIntermediateDumpObject:
|
||||||
|
Type GetType() const override;
|
||||||
|
|
||||||
|
using VectorType = std::vector<std::unique_ptr<const IOSIntermediateDumpMap>>;
|
||||||
|
VectorType::const_iterator begin() const { return list_.begin(); }
|
||||||
|
VectorType::const_iterator end() const { return list_.end(); }
|
||||||
|
VectorType::size_type size() const { return list_.size(); }
|
||||||
|
void push_back(std::unique_ptr<const IOSIntermediateDumpMap> val) {
|
||||||
|
list_.push_back(std::move(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
VectorType list_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(IOSIntermediateDumpList);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace crashpad
|
||||||
|
|
||||||
|
#endif // CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_LIST_H_
|
68
util/ios/ios_intermediate_dump_map.cc
Normal file
68
util/ios/ios_intermediate_dump_map.cc
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
// Copyright 2021 The Crashpad Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// 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 "util/ios/ios_intermediate_dump_map.h"
|
||||||
|
|
||||||
|
#include "util/ios/ios_intermediate_dump_data.h"
|
||||||
|
#include "util/ios/ios_intermediate_dump_list.h"
|
||||||
|
#include "util/ios/ios_intermediate_dump_object.h"
|
||||||
|
|
||||||
|
using crashpad::internal::IntermediateDumpKey;
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
IOSIntermediateDumpMap::IOSIntermediateDumpMap() : map_() {}
|
||||||
|
|
||||||
|
IOSIntermediateDumpMap::~IOSIntermediateDumpMap() {}
|
||||||
|
|
||||||
|
IOSIntermediateDumpMap::Type IOSIntermediateDumpMap::GetType() const {
|
||||||
|
return Type::kMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
const IOSIntermediateDumpData* IOSIntermediateDumpMap::GetAsData(
|
||||||
|
const IntermediateDumpKey& key) const {
|
||||||
|
auto object_it = map_.find(key);
|
||||||
|
if (object_it != map_.end()) {
|
||||||
|
IOSIntermediateDumpObject* object = object_it->second.get();
|
||||||
|
if (object->GetType() == Type::kData)
|
||||||
|
return static_cast<IOSIntermediateDumpData*>(object);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const IOSIntermediateDumpList* IOSIntermediateDumpMap::GetAsList(
|
||||||
|
const IntermediateDumpKey& key) const {
|
||||||
|
auto object_it = map_.find(key);
|
||||||
|
if (object_it != map_.end()) {
|
||||||
|
IOSIntermediateDumpObject* object = object_it->second.get();
|
||||||
|
if (object->GetType() == Type::kList)
|
||||||
|
return static_cast<IOSIntermediateDumpList*>(object);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const IOSIntermediateDumpMap* IOSIntermediateDumpMap::GetAsMap(
|
||||||
|
const IntermediateDumpKey& key) const {
|
||||||
|
auto object_it = map_.find(key);
|
||||||
|
if (object_it != map_.end()) {
|
||||||
|
IOSIntermediateDumpObject* object = object_it->second.get();
|
||||||
|
if (object->GetType() == Type::kMap)
|
||||||
|
return static_cast<IOSIntermediateDumpMap*>(object);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace crashpad
|
70
util/ios/ios_intermediate_dump_map.h
Normal file
70
util/ios/ios_intermediate_dump_map.h
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// Copyright 2021 The Crashpad Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// 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 CRASHPAD_UTIL_IOS_PACK_IOS_MAP_H_
|
||||||
|
#define CRASHPAD_UTIL_IOS_PACK_IOS_MAP_H_
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "base/macros.h"
|
||||||
|
#include "util/ios/ios_intermediate_dump_format.h"
|
||||||
|
#include "util/ios/ios_intermediate_dump_object.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
class IOSIntermediateDumpList;
|
||||||
|
class IOSIntermediateDumpData;
|
||||||
|
|
||||||
|
//! \brief A map object containing a IntermediateDump Key-Object pair.
|
||||||
|
//!
|
||||||
|
//! Also provides an element access helper.
|
||||||
|
class IOSIntermediateDumpMap : public IOSIntermediateDumpObject {
|
||||||
|
public:
|
||||||
|
IOSIntermediateDumpMap();
|
||||||
|
~IOSIntermediateDumpMap() override;
|
||||||
|
|
||||||
|
// IOSIntermediateDumpObject:
|
||||||
|
Type GetType() const override;
|
||||||
|
|
||||||
|
//! \brief Returns an IOSIntermediateDumpData. If the type is not kData,
|
||||||
|
//! returns nullptr
|
||||||
|
const IOSIntermediateDumpData* GetAsData(
|
||||||
|
const IntermediateDumpKey& key) const;
|
||||||
|
|
||||||
|
//! \brief Returns an IOSIntermediateDumpList. If the type is not kList,
|
||||||
|
//! returns nullptr
|
||||||
|
const IOSIntermediateDumpList* GetAsList(
|
||||||
|
const IntermediateDumpKey& key) const;
|
||||||
|
|
||||||
|
//! \brief Returns an IOSIntermediateDumpMap. If the type is not kMap,
|
||||||
|
//! returns nullptr
|
||||||
|
const IOSIntermediateDumpMap* GetAsMap(const IntermediateDumpKey& key) const;
|
||||||
|
|
||||||
|
//! \brief Returns `true` if the map is empty.
|
||||||
|
bool empty() const { return map_.empty(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class IOSIntermediateDumpReader;
|
||||||
|
std::map<IntermediateDumpKey, std::unique_ptr<IOSIntermediateDumpObject>>
|
||||||
|
map_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(IOSIntermediateDumpMap);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace crashpad
|
||||||
|
|
||||||
|
#endif // CRASHPAD_UTIL_IOS_PACK_IOS_MAP_H_
|
25
util/ios/ios_intermediate_dump_object.cc
Normal file
25
util/ios/ios_intermediate_dump_object.cc
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2021 The Crashpad Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// 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 "util/ios/ios_intermediate_dump_object.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
IOSIntermediateDumpObject::IOSIntermediateDumpObject() = default;
|
||||||
|
|
||||||
|
IOSIntermediateDumpObject::~IOSIntermediateDumpObject() {}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace crashpad
|
51
util/ios/ios_intermediate_dump_object.h
Normal file
51
util/ios/ios_intermediate_dump_object.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// Copyright 2021 The Crashpad Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// 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 CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_OBJECT_H_
|
||||||
|
#define CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_OBJECT_H_
|
||||||
|
|
||||||
|
#include "base/macros.h"
|
||||||
|
#include "util/ios/ios_intermediate_dump_writer.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
//! \brief Base class for intermediate dump object types.
|
||||||
|
class IOSIntermediateDumpObject {
|
||||||
|
public:
|
||||||
|
IOSIntermediateDumpObject();
|
||||||
|
virtual ~IOSIntermediateDumpObject();
|
||||||
|
|
||||||
|
//! \brief The type of object stored in the intermediate dump. .
|
||||||
|
enum class Type {
|
||||||
|
//! \brief A data object, containing array of bytes.
|
||||||
|
kData,
|
||||||
|
|
||||||
|
//! \brief A map object, containing other lists, maps and data objects.
|
||||||
|
kMap,
|
||||||
|
|
||||||
|
//! \brief A list object, containing a list of map objects.
|
||||||
|
kList,
|
||||||
|
};
|
||||||
|
|
||||||
|
//! \brief Returns a type.
|
||||||
|
virtual Type GetType() const = 0;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(IOSIntermediateDumpObject);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace crashpad
|
||||||
|
|
||||||
|
#endif // CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_OBJECT_H_
|
192
util/ios/ios_intermediate_dump_reader.cc
Normal file
192
util/ios/ios_intermediate_dump_reader.cc
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
// Copyright 2021 The Crashpad Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// 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 "util/ios/ios_intermediate_dump_reader.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <stack>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "base/logging.h"
|
||||||
|
#include "util/file/filesystem.h"
|
||||||
|
#include "util/ios/ios_intermediate_dump_data.h"
|
||||||
|
#include "util/ios/ios_intermediate_dump_format.h"
|
||||||
|
#include "util/ios/ios_intermediate_dump_list.h"
|
||||||
|
#include "util/ios/ios_intermediate_dump_object.h"
|
||||||
|
#include "util/ios/ios_intermediate_dump_writer.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
bool IOSIntermediateDumpReader::Initialize(const base::FilePath& path) {
|
||||||
|
ScopedFileHandle handle(LoggingOpenFileForRead(path));
|
||||||
|
auto reader = std::make_unique<WeakFileHandleFileReader>(handle.get());
|
||||||
|
|
||||||
|
// In the event a crash is introduced by this intermediate dump, don't ever
|
||||||
|
// read a file twice. To ensure this doesn't happen, immediately unlink.
|
||||||
|
LoggingRemoveFile(path);
|
||||||
|
|
||||||
|
// Don't initialize invalid or empty files.
|
||||||
|
FileOffset size = LoggingFileSizeByHandle(handle.get());
|
||||||
|
if (!handle.is_valid() || size == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Parse(reader.get(), size)) {
|
||||||
|
LOG(ERROR) << "Intermediate dump parsing failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IOSIntermediateDumpReader::Parse(FileReaderInterface* reader,
|
||||||
|
FileOffset file_size) {
|
||||||
|
std::stack<IOSIntermediateDumpObject*> stack;
|
||||||
|
stack.push(&minidump_);
|
||||||
|
using Command = IOSIntermediateDumpWriter::CommandType;
|
||||||
|
using Type = IOSIntermediateDumpObject::Type;
|
||||||
|
|
||||||
|
Command command;
|
||||||
|
if (!reader->ReadExactly(&command, sizeof(Command)) ||
|
||||||
|
command != Command::kRootMapStart) {
|
||||||
|
LOG(ERROR) << "Unexpected start to root map.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (reader->ReadExactly(&command, sizeof(Command))) {
|
||||||
|
IOSIntermediateDumpObject* parent = stack.top();
|
||||||
|
switch (command) {
|
||||||
|
case Command::kMapStart: {
|
||||||
|
std::unique_ptr<IOSIntermediateDumpMap> new_map(
|
||||||
|
new IOSIntermediateDumpMap());
|
||||||
|
if (parent->GetType() == Type::kMap) {
|
||||||
|
const auto parent_map = static_cast<IOSIntermediateDumpMap*>(parent);
|
||||||
|
stack.push(new_map.get());
|
||||||
|
IntermediateDumpKey key;
|
||||||
|
if (!reader->ReadExactly(&key, sizeof(key)))
|
||||||
|
return false;
|
||||||
|
if (key == IntermediateDumpKey::kInvalid)
|
||||||
|
return false;
|
||||||
|
parent_map->map_[key] = std::move(new_map);
|
||||||
|
} else if (parent->GetType() == Type::kList) {
|
||||||
|
const auto parent_list =
|
||||||
|
static_cast<IOSIntermediateDumpList*>(parent);
|
||||||
|
stack.push(new_map.get());
|
||||||
|
parent_list->push_back(std::move(new_map));
|
||||||
|
} else {
|
||||||
|
LOG(ERROR) << "Unexpected parent (not a map or list).";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Command::kArrayStart: {
|
||||||
|
auto new_list = std::make_unique<IOSIntermediateDumpList>();
|
||||||
|
if (parent->GetType() != Type::kMap) {
|
||||||
|
LOG(ERROR) << "Attempting to push an array not in a map.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IntermediateDumpKey key;
|
||||||
|
if (!reader->ReadExactly(&key, sizeof(key)))
|
||||||
|
return false;
|
||||||
|
if (key == IntermediateDumpKey::kInvalid)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto parent_map = static_cast<IOSIntermediateDumpMap*>(parent);
|
||||||
|
stack.push(new_list.get());
|
||||||
|
parent_map->map_[key] = std::move(new_list);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Command::kMapEnd:
|
||||||
|
if (stack.size() < 2) {
|
||||||
|
LOG(ERROR) << "Attempting to pop off main map.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent->GetType() != Type::kMap) {
|
||||||
|
LOG(ERROR) << "Unexpected map end not in a map.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
stack.pop();
|
||||||
|
break;
|
||||||
|
case Command::kArrayEnd:
|
||||||
|
if (stack.size() < 2) {
|
||||||
|
LOG(ERROR) << "Attempting to pop off main map.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (parent->GetType() != Type::kList) {
|
||||||
|
LOG(ERROR) << "Unexpected list end not in a list.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
stack.pop();
|
||||||
|
break;
|
||||||
|
case Command::kProperty: {
|
||||||
|
if (parent->GetType() != Type::kMap) {
|
||||||
|
LOG(ERROR) << "Attempting to add a property not in a map.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
IntermediateDumpKey key;
|
||||||
|
if (!reader->ReadExactly(&key, sizeof(key)))
|
||||||
|
return false;
|
||||||
|
if (key == IntermediateDumpKey::kInvalid)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
off_t value_length;
|
||||||
|
if (!reader->ReadExactly(&value_length, sizeof(value_length))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr int kMaximumPropertyLength = 64 * 1024 * 1024; // 64MB.
|
||||||
|
if (value_length > kMaximumPropertyLength) {
|
||||||
|
LOG(ERROR) << "Attempting to read a property that's too big: "
|
||||||
|
<< value_length;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> data(value_length);
|
||||||
|
if (!reader->ReadExactly(data.data(), value_length)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto parent_map = static_cast<IOSIntermediateDumpMap*>(parent);
|
||||||
|
if (parent_map->map_.find(key) != parent_map->map_.end()) {
|
||||||
|
LOG(ERROR) << "Inserting duplicate key";
|
||||||
|
}
|
||||||
|
parent_map->map_[key] =
|
||||||
|
std::make_unique<IOSIntermediateDumpData>(std::move(data));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Command::kRootMapEnd: {
|
||||||
|
if (stack.size() != 1) {
|
||||||
|
LOG(ERROR) << "Unexpected end of root map.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reader->Seek(0, SEEK_CUR) != file_size) {
|
||||||
|
LOG(ERROR) << "Root map ended before end of file.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
LOG(ERROR) << "Failed to parse serialized intermediate minidump.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(ERROR) << "Unexpected end of root map.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace crashpad
|
58
util/ios/ios_intermediate_dump_reader.h
Normal file
58
util/ios/ios_intermediate_dump_reader.h
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// Copyright 2021 The Crashpad Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// 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 CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_READER_H_
|
||||||
|
#define CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_READER_H_
|
||||||
|
|
||||||
|
#include "base/files/file_path.h"
|
||||||
|
#include "base/macros.h"
|
||||||
|
#include "util/file/file_reader.h"
|
||||||
|
#include "util/ios/ios_intermediate_dump_map.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
//! \brief Open and parse iOS intermediate dumps.
|
||||||
|
class IOSIntermediateDumpReader {
|
||||||
|
public:
|
||||||
|
IOSIntermediateDumpReader() {}
|
||||||
|
|
||||||
|
//! \brief Open and parses \a path, ignoring empty files.
|
||||||
|
//!
|
||||||
|
//! Will attempt to parse the binary file, similar to a JSON file, using the
|
||||||
|
//! same format used by IOSIntermediateDumpWriter, resulting in an
|
||||||
|
//! IOSIntermediateDumpMap
|
||||||
|
//!
|
||||||
|
//! \param[in] path The intermediate dump to read.
|
||||||
|
//!
|
||||||
|
//! \return On success, returns `true`, otherwise returns `false`. Clients may
|
||||||
|
//! still attempt to parse RootMap, as partial minidumps may still be
|
||||||
|
//! usable.
|
||||||
|
bool Initialize(const base::FilePath& path);
|
||||||
|
|
||||||
|
//! \brief Returns an IOSIntermediateDumpMap corresponding to the root of the
|
||||||
|
//! intermediate dump.
|
||||||
|
const IOSIntermediateDumpMap* RootMap() { return &minidump_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool Parse(FileReaderInterface* reader, FileOffset file_size);
|
||||||
|
IOSIntermediateDumpMap minidump_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(IOSIntermediateDumpReader);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace crashpad
|
||||||
|
|
||||||
|
#endif // CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_READER_H_
|
238
util/ios/ios_intermediate_dump_reader_test.cc
Normal file
238
util/ios/ios_intermediate_dump_reader_test.cc
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
// Copyright 2021 The Crashpad Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// 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 "util/ios/ios_intermediate_dump_reader.h"
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <mach/vm_map.h>
|
||||||
|
|
||||||
|
#include "base/posix/eintr_wrapper.h"
|
||||||
|
#include "gmock/gmock.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "test/errors.h"
|
||||||
|
#include "test/scoped_temp_dir.h"
|
||||||
|
#include "util/file/filesystem.h"
|
||||||
|
#include "util/ios/ios_intermediate_dump_data.h"
|
||||||
|
#include "util/ios/ios_intermediate_dump_format.h"
|
||||||
|
#include "util/ios/ios_intermediate_dump_list.h"
|
||||||
|
#include "util/ios/ios_intermediate_dump_writer.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
namespace test {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using Key = internal::IntermediateDumpKey;
|
||||||
|
using internal::IOSIntermediateDumpWriter;
|
||||||
|
|
||||||
|
class IOSIntermediateDumpReaderTest : public testing::Test {
|
||||||
|
protected:
|
||||||
|
// testing::Test:
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
path_ = temp_dir_.path().Append("dump_file");
|
||||||
|
fd_ = base::ScopedFD(HANDLE_EINTR(
|
||||||
|
::open(path_.value().c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644)));
|
||||||
|
ASSERT_GE(fd_.get(), 0) << ErrnoMessage("open");
|
||||||
|
|
||||||
|
writer_ = std::make_unique<IOSIntermediateDumpWriter>();
|
||||||
|
ASSERT_TRUE(writer_->Open(path_));
|
||||||
|
ASSERT_TRUE(IsRegularFile(path_));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() override {
|
||||||
|
fd_.reset();
|
||||||
|
writer_.reset();
|
||||||
|
EXPECT_FALSE(IsRegularFile(path_));
|
||||||
|
}
|
||||||
|
|
||||||
|
int fd() { return fd_.get(); }
|
||||||
|
|
||||||
|
const base::FilePath& path() const { return path_; }
|
||||||
|
|
||||||
|
std::unique_ptr<IOSIntermediateDumpWriter> writer_;
|
||||||
|
|
||||||
|
private:
|
||||||
|
base::ScopedFD fd_;
|
||||||
|
ScopedTempDir temp_dir_;
|
||||||
|
base::FilePath path_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(IOSIntermediateDumpReaderTest, ReadNoFile) {
|
||||||
|
internal::IOSIntermediateDumpReader reader;
|
||||||
|
EXPECT_FALSE(reader.Initialize(base::FilePath()));
|
||||||
|
EXPECT_TRUE(LoggingRemoveFile(path()));
|
||||||
|
EXPECT_FALSE(IsRegularFile(path()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(IOSIntermediateDumpReaderTest, ReadEmptyFile) {
|
||||||
|
internal::IOSIntermediateDumpReader reader;
|
||||||
|
EXPECT_FALSE(reader.Initialize(path()));
|
||||||
|
EXPECT_FALSE(IsRegularFile(path()));
|
||||||
|
|
||||||
|
const auto root_map = reader.RootMap();
|
||||||
|
EXPECT_TRUE(root_map->empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(IOSIntermediateDumpReaderTest, ReadHelloWorld) {
|
||||||
|
std::string hello_world("hello world.");
|
||||||
|
EXPECT_TRUE(
|
||||||
|
LoggingWriteFile(fd(), hello_world.c_str(), hello_world.length()));
|
||||||
|
internal::IOSIntermediateDumpReader reader;
|
||||||
|
EXPECT_TRUE(reader.Initialize(path()));
|
||||||
|
EXPECT_FALSE(IsRegularFile(path()));
|
||||||
|
|
||||||
|
const auto root_map = reader.RootMap();
|
||||||
|
EXPECT_TRUE(root_map->empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(IOSIntermediateDumpReaderTest, WriteBadPropertyDataLength) {
|
||||||
|
internal::IOSIntermediateDumpReader reader;
|
||||||
|
IOSIntermediateDumpWriter::CommandType command_type =
|
||||||
|
IOSIntermediateDumpWriter::CommandType::kRootMapStart;
|
||||||
|
EXPECT_TRUE(LoggingWriteFile(fd(), &command_type, sizeof(command_type)));
|
||||||
|
|
||||||
|
command_type = IOSIntermediateDumpWriter::CommandType::kProperty;
|
||||||
|
EXPECT_TRUE(LoggingWriteFile(fd(), &command_type, sizeof(command_type)));
|
||||||
|
Key key = Key::kVersion;
|
||||||
|
EXPECT_TRUE(LoggingWriteFile(fd(), &key, sizeof(key)));
|
||||||
|
uint8_t value = 1;
|
||||||
|
size_t value_length = 999999;
|
||||||
|
EXPECT_TRUE(LoggingWriteFile(fd(), &value_length, sizeof(size_t)));
|
||||||
|
EXPECT_TRUE(LoggingWriteFile(fd(), &value, sizeof(value)));
|
||||||
|
EXPECT_TRUE(reader.Initialize(path()));
|
||||||
|
EXPECT_FALSE(IsRegularFile(path()));
|
||||||
|
|
||||||
|
const auto root_map = reader.RootMap();
|
||||||
|
EXPECT_TRUE(root_map->empty());
|
||||||
|
const auto version_data = root_map->GetAsData(Key::kVersion);
|
||||||
|
EXPECT_EQ(version_data, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(IOSIntermediateDumpReaderTest, InvalidArrayInArray) {
|
||||||
|
internal::IOSIntermediateDumpReader reader;
|
||||||
|
{
|
||||||
|
IOSIntermediateDumpWriter::ScopedRootMap scopedRoot(writer_.get());
|
||||||
|
IOSIntermediateDumpWriter::ScopedArray threadArray(writer_.get(),
|
||||||
|
Key::kThreads);
|
||||||
|
IOSIntermediateDumpWriter::ScopedArray innerThreadArray(writer_.get(),
|
||||||
|
Key::kModules);
|
||||||
|
|
||||||
|
// Write version last, so it's not parsed.
|
||||||
|
int8_t version = 1;
|
||||||
|
writer_->AddProperty(Key::kVersion, &version);
|
||||||
|
}
|
||||||
|
EXPECT_TRUE(writer_->Close());
|
||||||
|
EXPECT_TRUE(reader.Initialize(path()));
|
||||||
|
EXPECT_FALSE(IsRegularFile(path()));
|
||||||
|
|
||||||
|
const auto root_map = reader.RootMap();
|
||||||
|
EXPECT_FALSE(root_map->empty());
|
||||||
|
const auto version_data = root_map->GetAsData(Key::kVersion);
|
||||||
|
EXPECT_EQ(version_data, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(IOSIntermediateDumpReaderTest, InvalidPropertyInArray) {
|
||||||
|
internal::IOSIntermediateDumpReader reader;
|
||||||
|
|
||||||
|
{
|
||||||
|
IOSIntermediateDumpWriter::ScopedRootMap scopedRoot(writer_.get());
|
||||||
|
IOSIntermediateDumpWriter::ScopedArray threadArray(writer_.get(),
|
||||||
|
Key::kThreads);
|
||||||
|
|
||||||
|
// Write version last, so it's not parsed.
|
||||||
|
int8_t version = 1;
|
||||||
|
writer_->AddProperty(Key::kVersion, &version);
|
||||||
|
}
|
||||||
|
EXPECT_TRUE(writer_->Close());
|
||||||
|
EXPECT_TRUE(reader.Initialize(path()));
|
||||||
|
EXPECT_FALSE(IsRegularFile(path()));
|
||||||
|
|
||||||
|
const auto root_map = reader.RootMap();
|
||||||
|
EXPECT_FALSE(root_map->empty());
|
||||||
|
const auto version_data = root_map->GetAsData(Key::kVersion);
|
||||||
|
EXPECT_EQ(version_data, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(IOSIntermediateDumpReaderTest, ReadValidData) {
|
||||||
|
internal::IOSIntermediateDumpReader reader;
|
||||||
|
uint8_t version = 1;
|
||||||
|
{
|
||||||
|
IOSIntermediateDumpWriter::ScopedRootMap scopedRoot(writer_.get());
|
||||||
|
EXPECT_TRUE(writer_->AddProperty(Key::kVersion, &version));
|
||||||
|
{
|
||||||
|
IOSIntermediateDumpWriter::ScopedArray threadArray(
|
||||||
|
writer_.get(), Key::kThreadContextMemoryRegions);
|
||||||
|
IOSIntermediateDumpWriter::ScopedArrayMap threadMap(writer_.get());
|
||||||
|
|
||||||
|
std::string random_data("random_data");
|
||||||
|
EXPECT_TRUE(writer_->AddProperty(Key::kThreadContextMemoryRegionAddress,
|
||||||
|
&version));
|
||||||
|
EXPECT_TRUE(writer_->AddProperty(Key::kThreadContextMemoryRegionData,
|
||||||
|
random_data.c_str(),
|
||||||
|
random_data.length()));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
IOSIntermediateDumpWriter::ScopedMap map(writer_.get(),
|
||||||
|
Key::kProcessInfo);
|
||||||
|
pid_t p_pid = getpid();
|
||||||
|
EXPECT_TRUE(writer_->AddProperty(Key::kPID, &p_pid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_TRUE(writer_->Close());
|
||||||
|
EXPECT_TRUE(reader.Initialize(path()));
|
||||||
|
EXPECT_FALSE(IsRegularFile(path()));
|
||||||
|
|
||||||
|
auto root_map = reader.RootMap();
|
||||||
|
EXPECT_FALSE(root_map->empty());
|
||||||
|
version = -1;
|
||||||
|
const auto version_data = root_map->GetAsData(Key::kVersion);
|
||||||
|
ASSERT_NE(version_data, nullptr);
|
||||||
|
EXPECT_TRUE(version_data->GetValue<uint8_t>(&version));
|
||||||
|
EXPECT_EQ(version, 1);
|
||||||
|
|
||||||
|
const auto process_info = root_map->GetAsMap(Key::kProcessInfo);
|
||||||
|
ASSERT_NE(process_info, nullptr);
|
||||||
|
const auto pid_data = process_info->GetAsData(Key::kPID);
|
||||||
|
ASSERT_NE(pid_data, nullptr);
|
||||||
|
pid_t p_pid = -1;
|
||||||
|
EXPECT_TRUE(pid_data->GetValue<pid_t>(&p_pid));
|
||||||
|
ASSERT_EQ(p_pid, getpid());
|
||||||
|
|
||||||
|
const auto thread_context_memory_regions =
|
||||||
|
root_map->GetAsList(Key::kThreadContextMemoryRegions);
|
||||||
|
EXPECT_EQ(thread_context_memory_regions->size(), 1UL);
|
||||||
|
for (const auto& region : *thread_context_memory_regions) {
|
||||||
|
const auto data = region->GetAsData(Key::kThreadContextMemoryRegionData);
|
||||||
|
ASSERT_NE(data, nullptr);
|
||||||
|
// Load as string.
|
||||||
|
EXPECT_EQ(data->GetString(), "random_data");
|
||||||
|
|
||||||
|
// Load as bytes.
|
||||||
|
auto bytes = data->bytes();
|
||||||
|
vm_size_t data_size = bytes.size();
|
||||||
|
EXPECT_EQ(data_size, 11UL);
|
||||||
|
|
||||||
|
const char* data_bytes = reinterpret_cast<const char*>(bytes.data());
|
||||||
|
EXPECT_EQ(std::string(data_bytes, data_size), "random_data");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto system_info = root_map->GetAsMap(Key::kSystemInfo);
|
||||||
|
EXPECT_EQ(system_info, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace test
|
||||||
|
} // namespace crashpad
|
137
util/ios/ios_intermediate_dump_writer.cc
Normal file
137
util/ios/ios_intermediate_dump_writer.cc
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
// Copyright 2021 The Crashpad Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// 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 "util/ios/ios_intermediate_dump_writer.h"
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "base/posix/eintr_wrapper.h"
|
||||||
|
#include "build/build_config.h"
|
||||||
|
#include "util/ios/raw_logging.h"
|
||||||
|
#include "util/ios/scoped_vm_read.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// Similar to LoggingWriteFile but with CRASHPAD_RAW_LOG.
|
||||||
|
bool RawLoggingWriteFile(int fd, const void* buffer, size_t size) {
|
||||||
|
uintptr_t buffer_int = reinterpret_cast<uintptr_t>(buffer);
|
||||||
|
while (size > 0) {
|
||||||
|
ssize_t bytes_written = HANDLE_EINTR(
|
||||||
|
write(fd, reinterpret_cast<const char*>(buffer_int), size));
|
||||||
|
if (bytes_written < 0 || bytes_written == 0) {
|
||||||
|
CRASHPAD_RAW_LOG_ERROR(bytes_written, "RawLoggingWriteFile");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
buffer_int += bytes_written;
|
||||||
|
size -= bytes_written;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Similar to LoggingCloseFile but with CRASHPAD_RAW_LOG.
|
||||||
|
bool RawLoggingCloseFile(int fd) {
|
||||||
|
int rv = IGNORE_EINTR(close(fd));
|
||||||
|
if (rv != 0) {
|
||||||
|
CRASHPAD_RAW_LOG_ERROR(rv, "RawLoggingCloseFile");
|
||||||
|
}
|
||||||
|
return rv == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Similar to LoggingLockFile but with CRASHPAD_RAW_LOG.
|
||||||
|
bool RawLoggingLockFileExclusiveNonBlocking(int fd) {
|
||||||
|
int rv = HANDLE_EINTR(flock(fd, LOCK_EX | LOCK_NB));
|
||||||
|
if (rv != 0) {
|
||||||
|
CRASHPAD_RAW_LOG_ERROR(rv, "RawLoggingLockFileExclusiveNonBlocking");
|
||||||
|
}
|
||||||
|
return rv == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IOSIntermediateDumpWriter::Open(const base::FilePath& path) {
|
||||||
|
// Set data protection class D (No protection). A file with this type of
|
||||||
|
// protection can be read from or written to at any time.
|
||||||
|
// See:
|
||||||
|
// https://support.apple.com/guide/security/data-protection-classes-secb010e978a/web
|
||||||
|
constexpr int PROTECTION_CLASS_D = 4;
|
||||||
|
fd_ = HANDLE_EINTR(open_dprotected_np(path.value().c_str(),
|
||||||
|
O_WRONLY | O_CREAT | O_TRUNC,
|
||||||
|
PROTECTION_CLASS_D,
|
||||||
|
0 /* dpflags */,
|
||||||
|
0644 /* mode */));
|
||||||
|
if (fd_ < 0) {
|
||||||
|
CRASHPAD_RAW_LOG_ERROR(fd_, "open intermediate dump");
|
||||||
|
CRASHPAD_RAW_LOG(path.value().c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RawLoggingLockFileExclusiveNonBlocking(fd_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IOSIntermediateDumpWriter::Close() {
|
||||||
|
return RawLoggingCloseFile(fd_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IOSIntermediateDumpWriter::ArrayMapStart() {
|
||||||
|
const CommandType command_type = CommandType::kMapStart;
|
||||||
|
return RawLoggingWriteFile(fd_, &command_type, sizeof(command_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IOSIntermediateDumpWriter::MapStart(IntermediateDumpKey key) {
|
||||||
|
const CommandType command_type = CommandType::kMapStart;
|
||||||
|
return RawLoggingWriteFile(fd_, &command_type, sizeof(command_type)) &&
|
||||||
|
RawLoggingWriteFile(fd_, &key, sizeof(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IOSIntermediateDumpWriter::ArrayStart(IntermediateDumpKey key) {
|
||||||
|
const CommandType command_type = CommandType::kArrayStart;
|
||||||
|
return RawLoggingWriteFile(fd_, &command_type, sizeof(command_type)) &&
|
||||||
|
RawLoggingWriteFile(fd_, &key, sizeof(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IOSIntermediateDumpWriter::MapEnd() {
|
||||||
|
const CommandType command_type = CommandType::kMapEnd;
|
||||||
|
return RawLoggingWriteFile(fd_, &command_type, sizeof(command_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IOSIntermediateDumpWriter::ArrayEnd() {
|
||||||
|
const CommandType command_type = CommandType::kArrayEnd;
|
||||||
|
return RawLoggingWriteFile(fd_, &command_type, sizeof(command_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IOSIntermediateDumpWriter::RootMapStart() {
|
||||||
|
const CommandType command_type = CommandType::kRootMapStart;
|
||||||
|
return RawLoggingWriteFile(fd_, &command_type, sizeof(command_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IOSIntermediateDumpWriter::RootMapEnd() {
|
||||||
|
const CommandType command_type = CommandType::kRootMapEnd;
|
||||||
|
return RawLoggingWriteFile(fd_, &command_type, sizeof(command_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IOSIntermediateDumpWriter::AddPropertyInternal(IntermediateDumpKey key,
|
||||||
|
const char* value,
|
||||||
|
size_t value_length) {
|
||||||
|
ScopedVMRead<char> vmread;
|
||||||
|
if (!vmread.Read(value, value_length))
|
||||||
|
return false;
|
||||||
|
const CommandType command_type = CommandType::kProperty;
|
||||||
|
return RawLoggingWriteFile(fd_, &command_type, sizeof(command_type)) &&
|
||||||
|
RawLoggingWriteFile(fd_, &key, sizeof(key)) &&
|
||||||
|
RawLoggingWriteFile(fd_, &value_length, sizeof(size_t)) &&
|
||||||
|
RawLoggingWriteFile(fd_, vmread.get(), value_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace crashpad
|
197
util/ios/ios_intermediate_dump_writer.h
Normal file
197
util/ios/ios_intermediate_dump_writer.h
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
// Copyright 2021 The Crashpad Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// 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 CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_WRITER_H_
|
||||||
|
#define CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_WRITER_H_
|
||||||
|
|
||||||
|
#include "base/files/file_path.h"
|
||||||
|
#include "base/macros.h"
|
||||||
|
#include "util/ios/ios_intermediate_dump_format.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
//! \brief Wrapper class for writing intermediate dump file.
|
||||||
|
//!
|
||||||
|
//! Due to the limitations of in-process handling, an intermediate dump file is
|
||||||
|
//! written during exceptions. The data is streamed to a file using only
|
||||||
|
//! in-process safe methods.
|
||||||
|
//!
|
||||||
|
//! The file format is similar to binary JSON, supporting keyed properties, maps
|
||||||
|
//! and arrays.
|
||||||
|
//! - Property [key:int, length:int, value:intarray]
|
||||||
|
//! - StartMap [key:int], followed by repeating Properties until EndMap
|
||||||
|
//! - StartArray [key:int], followed by repeating Maps until EndArray
|
||||||
|
//! - EndMap, EndArray, EndDocument
|
||||||
|
//!
|
||||||
|
//! Similar to JSON, maps can contain other maps, arrays and properties.
|
||||||
|
//!
|
||||||
|
//! Note: All methods are `RUNS-DURING-CRASH`.
|
||||||
|
class IOSIntermediateDumpWriter final {
|
||||||
|
public:
|
||||||
|
IOSIntermediateDumpWriter() = default;
|
||||||
|
|
||||||
|
//! \brief Command instructions for the intermediate dump reader.
|
||||||
|
enum class CommandType : uint8_t {
|
||||||
|
//! \brief Indicates a new map, followed by associated key.
|
||||||
|
kMapStart = 0x01,
|
||||||
|
|
||||||
|
//! \brief Indicates map is complete.
|
||||||
|
kMapEnd = 0x02,
|
||||||
|
|
||||||
|
//! \brief Indicates a new array, followed by associated key.
|
||||||
|
kArrayStart = 0x03,
|
||||||
|
|
||||||
|
//! \brief Indicates array is complete.
|
||||||
|
kArrayEnd = 0x04,
|
||||||
|
|
||||||
|
//! \brief Indicates a new property, followed by a key, length and value.
|
||||||
|
kProperty = 0x05,
|
||||||
|
|
||||||
|
//! \brief Indicates the start of the root map.
|
||||||
|
kRootMapStart = 0x06,
|
||||||
|
|
||||||
|
//! \brief Indicates the end of the root map, and that there is nothing left
|
||||||
|
//! to parse.
|
||||||
|
kRootMapEnd = 0x07,
|
||||||
|
};
|
||||||
|
|
||||||
|
//! \brief Open and lock an intermediate dump file. This is the only method
|
||||||
|
//! in the writer class that is generally run outside of a crash.
|
||||||
|
//!
|
||||||
|
//! \param[in] path The path to the intermediate dump.
|
||||||
|
//!
|
||||||
|
//! \return On success, returns `true`, otherwise returns `false`.
|
||||||
|
bool Open(const base::FilePath& path);
|
||||||
|
|
||||||
|
//! \brief Completes writing the intermediate dump file and releases the
|
||||||
|
//! file handle.
|
||||||
|
//!
|
||||||
|
//! \return On success, returns `true`, otherwise returns `false`.
|
||||||
|
bool Close();
|
||||||
|
|
||||||
|
//! \brief A scoped wrapper for calls to RootMapStart and RootMapEnd.
|
||||||
|
class ScopedRootMap {
|
||||||
|
public:
|
||||||
|
explicit ScopedRootMap(IOSIntermediateDumpWriter* writer)
|
||||||
|
: writer_(writer) {
|
||||||
|
writer->RootMapStart();
|
||||||
|
}
|
||||||
|
~ScopedRootMap() { writer_->RootMapEnd(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
IOSIntermediateDumpWriter* writer_;
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(ScopedRootMap);
|
||||||
|
};
|
||||||
|
|
||||||
|
//! \brief A scoped wrapper for calls to MapStart and MapEnd.
|
||||||
|
class ScopedMap {
|
||||||
|
public:
|
||||||
|
explicit ScopedMap(IOSIntermediateDumpWriter* writer,
|
||||||
|
IntermediateDumpKey key)
|
||||||
|
: writer_(writer) {
|
||||||
|
writer->MapStart(key);
|
||||||
|
}
|
||||||
|
~ScopedMap() { writer_->MapEnd(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
IOSIntermediateDumpWriter* writer_;
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(ScopedMap);
|
||||||
|
};
|
||||||
|
|
||||||
|
//! \brief A scoped wrapper for calls to ArrayMapStart and MapEnd.
|
||||||
|
class ScopedArrayMap {
|
||||||
|
public:
|
||||||
|
explicit ScopedArrayMap(IOSIntermediateDumpWriter* writer)
|
||||||
|
: writer_(writer) {
|
||||||
|
writer->ArrayMapStart();
|
||||||
|
}
|
||||||
|
~ScopedArrayMap() { writer_->MapEnd(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
IOSIntermediateDumpWriter* writer_;
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(ScopedArrayMap);
|
||||||
|
};
|
||||||
|
|
||||||
|
//! \brief A scoped wrapper for calls to ArrayStart and ArrayEnd.
|
||||||
|
class ScopedArray {
|
||||||
|
public:
|
||||||
|
explicit ScopedArray(IOSIntermediateDumpWriter* writer,
|
||||||
|
IntermediateDumpKey key)
|
||||||
|
: writer_(writer) {
|
||||||
|
writer->ArrayStart(key);
|
||||||
|
}
|
||||||
|
~ScopedArray() { writer_->ArrayEnd(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
IOSIntermediateDumpWriter* writer_;
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(ScopedArray);
|
||||||
|
};
|
||||||
|
|
||||||
|
//! \return The `true` if able to AddPropertyInternal the \a key \a value
|
||||||
|
//! \a count tuple.
|
||||||
|
template <typename T>
|
||||||
|
bool AddProperty(IntermediateDumpKey key, const T* value, size_t count = 1) {
|
||||||
|
return AddPropertyInternal(
|
||||||
|
key, reinterpret_cast<const char*>(value), count * sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \return The `true` if able to AddPropertyInternal the \a key \a value
|
||||||
|
//! \a count tuple.
|
||||||
|
bool AddPropertyBytes(IntermediateDumpKey key,
|
||||||
|
const void* value,
|
||||||
|
size_t value_length) {
|
||||||
|
return AddPropertyInternal(
|
||||||
|
key, reinterpret_cast<const char*>(value), value_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
//! \return Returns `true` if able to write a kProperty command with the
|
||||||
|
//! \a key \a value \a count tuple.
|
||||||
|
bool AddPropertyInternal(IntermediateDumpKey key,
|
||||||
|
const char* value,
|
||||||
|
size_t value_length);
|
||||||
|
|
||||||
|
//! \return Returns `true` if able to write a kArrayStart command with the
|
||||||
|
//! \a key.
|
||||||
|
bool ArrayStart(IntermediateDumpKey key);
|
||||||
|
|
||||||
|
//! \return Returns `true` if able to write a kMapStart command with the
|
||||||
|
//! \a key.
|
||||||
|
bool MapStart(IntermediateDumpKey key);
|
||||||
|
|
||||||
|
//! \return Returns `true` if able to write a kMapStart command.
|
||||||
|
bool ArrayMapStart();
|
||||||
|
|
||||||
|
//! \return Returns `true` if able to write a kArrayEnd command.
|
||||||
|
bool ArrayEnd();
|
||||||
|
|
||||||
|
//! \return Returns `true` if able to write a kMapEnd command.
|
||||||
|
bool MapEnd();
|
||||||
|
|
||||||
|
//! \return Returns `true` if able to write a kRootMapStart command.
|
||||||
|
bool RootMapStart();
|
||||||
|
|
||||||
|
//! \return Returns `true` if able to write a kRootMapEnd command.
|
||||||
|
bool RootMapEnd();
|
||||||
|
|
||||||
|
int fd_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(IOSIntermediateDumpWriter);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace crashpad
|
||||||
|
|
||||||
|
#endif // CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_WRITER_H_
|
131
util/ios/ios_intermediate_dump_writer_test.cc
Normal file
131
util/ios/ios_intermediate_dump_writer_test.cc
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
// Copyright 2021 The Crashpad Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// 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 "util/ios/ios_intermediate_dump_writer.h"
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "base/files/scoped_file.h"
|
||||||
|
#include "base/posix/eintr_wrapper.h"
|
||||||
|
#include "gmock/gmock.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "test/errors.h"
|
||||||
|
#include "test/scoped_temp_dir.h"
|
||||||
|
#include "util/file/file_io.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
namespace test {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using Key = internal::IntermediateDumpKey;
|
||||||
|
using internal::IOSIntermediateDumpWriter;
|
||||||
|
|
||||||
|
class IOSIntermediateDumpWriterTest : public testing::Test {
|
||||||
|
protected:
|
||||||
|
// testing::Test:
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
path_ = temp_dir_.path().Append("dump_file");
|
||||||
|
writer_ = std::make_unique<IOSIntermediateDumpWriter>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() override {
|
||||||
|
writer_.reset();
|
||||||
|
EXPECT_EQ(unlink(path_.value().c_str()), 0) << ErrnoMessage("unlink");
|
||||||
|
}
|
||||||
|
|
||||||
|
const base::FilePath& path() const { return path_; }
|
||||||
|
|
||||||
|
std::unique_ptr<IOSIntermediateDumpWriter> writer_;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ScopedTempDir temp_dir_;
|
||||||
|
base::FilePath path_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test file is locked.
|
||||||
|
TEST_F(IOSIntermediateDumpWriterTest, OpenLocked) {
|
||||||
|
EXPECT_TRUE(writer_->Open(path()));
|
||||||
|
|
||||||
|
ScopedFileHandle handle(LoggingOpenFileForRead(path()));
|
||||||
|
EXPECT_TRUE(handle.is_valid());
|
||||||
|
EXPECT_EQ(LoggingLockFile(handle.get(),
|
||||||
|
FileLocking::kExclusive,
|
||||||
|
FileLockingBlocking::kNonBlocking),
|
||||||
|
FileLockingResult::kWouldBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(IOSIntermediateDumpWriterTest, Close) {
|
||||||
|
EXPECT_TRUE(writer_->Open(path()));
|
||||||
|
EXPECT_TRUE(writer_->Close());
|
||||||
|
|
||||||
|
std::string contents;
|
||||||
|
ASSERT_TRUE(LoggingReadEntireFile(path(), &contents));
|
||||||
|
ASSERT_EQ(contents, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(IOSIntermediateDumpWriterTest, ScopedArray) {
|
||||||
|
EXPECT_TRUE(writer_->Open(path()));
|
||||||
|
{
|
||||||
|
IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer_.get());
|
||||||
|
IOSIntermediateDumpWriter::ScopedArray threadArray(writer_.get(),
|
||||||
|
Key::kThreads);
|
||||||
|
IOSIntermediateDumpWriter::ScopedArrayMap threadMap(writer_.get());
|
||||||
|
}
|
||||||
|
std::string contents;
|
||||||
|
ASSERT_TRUE(LoggingReadEntireFile(path(), &contents));
|
||||||
|
std::string result("\6\x3p\x17\1\2\4\a", 8);
|
||||||
|
ASSERT_EQ(contents, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(IOSIntermediateDumpWriterTest, ScopedMap) {
|
||||||
|
EXPECT_TRUE(writer_->Open(path()));
|
||||||
|
{
|
||||||
|
IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer_.get());
|
||||||
|
IOSIntermediateDumpWriter::ScopedMap map(writer_.get(),
|
||||||
|
Key::kMachException);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string contents;
|
||||||
|
ASSERT_TRUE(LoggingReadEntireFile(path(), &contents));
|
||||||
|
std::string result("\6\1\xe8\3\2\a", 6);
|
||||||
|
ASSERT_EQ(contents, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(IOSIntermediateDumpWriterTest, Property) {
|
||||||
|
EXPECT_TRUE(writer_->Open(path()));
|
||||||
|
EXPECT_TRUE(writer_->AddProperty(Key::kVersion, "version", 7));
|
||||||
|
|
||||||
|
std::string contents;
|
||||||
|
ASSERT_TRUE(LoggingReadEntireFile(path(), &contents));
|
||||||
|
std::string result("\5\1\0\a\0\0\0\0\0\0\0version", 18);
|
||||||
|
ASSERT_EQ(contents, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(IOSIntermediateDumpWriterTest, BadProperty) {
|
||||||
|
EXPECT_TRUE(writer_->Open(path()));
|
||||||
|
ASSERT_FALSE(writer_->AddProperty(Key::kVersion, "version", -1));
|
||||||
|
|
||||||
|
std::string contents;
|
||||||
|
ASSERT_TRUE(LoggingReadEntireFile(path(), &contents));
|
||||||
|
|
||||||
|
// path() is now invalid, as type, key and value were written, but the
|
||||||
|
// value itself is not.
|
||||||
|
std::string results("\5\1\0\xff\xff\xff\xff\xff\xff\xff\xff", 11);
|
||||||
|
ASSERT_EQ(contents, results);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace test
|
||||||
|
} // namespace crashpad
|
Loading…
x
Reference in New Issue
Block a user