Calin Juravle | 31f2c15 | 2015-10-23 17:56:15 +0100 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright (C) 2015 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #include "offline_profiling_info.h" |
| 18 | |
| 19 | #include <fstream> |
| 20 | #include <set> |
| 21 | #include <sys/file.h> |
| 22 | #include <sys/stat.h> |
| 23 | #include <sys/uio.h> |
| 24 | |
| 25 | #include "art_method-inl.h" |
| 26 | #include "base/mutex.h" |
| 27 | #include "jit/profiling_info.h" |
| 28 | #include "safe_map.h" |
| 29 | #include "utils.h" |
| 30 | |
| 31 | namespace art { |
| 32 | |
| 33 | // An arbitrary value to throttle save requests. Set to 500ms for now. |
| 34 | static constexpr const uint64_t kMilisecondsToNano = 1000000; |
| 35 | static constexpr const uint64_t kMinimumTimeBetweenSavesNs = 500 * kMilisecondsToNano; |
| 36 | |
| 37 | bool OfflineProfilingInfo::NeedsSaving(uint64_t last_update_time_ns) const { |
| 38 | return last_update_time_ns - last_update_time_ns_.LoadRelaxed() > kMinimumTimeBetweenSavesNs; |
| 39 | } |
| 40 | |
| 41 | void OfflineProfilingInfo::SaveProfilingInfo(const std::string& filename, |
| 42 | uint64_t last_update_time_ns, |
| 43 | const std::set<ArtMethod*>& methods) { |
| 44 | if (!NeedsSaving(last_update_time_ns)) { |
| 45 | VLOG(profiler) << "No need to saved profile info to " << filename; |
| 46 | return; |
| 47 | } |
| 48 | |
| 49 | if (methods.empty()) { |
| 50 | VLOG(profiler) << "No info to save to " << filename; |
| 51 | return; |
| 52 | } |
| 53 | |
| 54 | DexFileToMethodsMap info; |
| 55 | { |
| 56 | ScopedObjectAccess soa(Thread::Current()); |
| 57 | for (auto it = methods.begin(); it != methods.end(); it++) { |
| 58 | AddMethodInfo(*it, &info); |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | // This doesn't need locking because we are trying to lock the file for exclusive |
| 63 | // access and fail immediately if we can't. |
| 64 | if (Serialize(filename, info)) { |
| 65 | last_update_time_ns_.StoreRelaxed(last_update_time_ns); |
| 66 | VLOG(profiler) << "Successfully saved profile info to " |
| 67 | << filename << " with time stamp: " << last_update_time_ns; |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | |
| 72 | void OfflineProfilingInfo::AddMethodInfo(ArtMethod* method, DexFileToMethodsMap* info) { |
| 73 | DCHECK(method != nullptr); |
| 74 | const DexFile* dex_file = method->GetDexFile(); |
| 75 | |
| 76 | auto info_it = info->find(dex_file); |
| 77 | if (info_it == info->end()) { |
| 78 | info_it = info->Put(dex_file, std::set<uint32_t>()); |
| 79 | } |
| 80 | info_it->second.insert(method->GetDexMethodIndex()); |
| 81 | } |
| 82 | |
| 83 | static int OpenOrCreateFile(const std::string& filename) { |
| 84 | // TODO(calin) allow the shared uid of the app to access the file. |
| 85 | int fd = open(filename.c_str(), |
| 86 | O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW | O_CLOEXEC, |
| 87 | S_IRUSR | S_IWUSR); |
| 88 | if (fd < 0) { |
| 89 | PLOG(WARNING) << "Failed to open profile file " << filename; |
| 90 | return -1; |
| 91 | } |
| 92 | |
| 93 | // Lock the file for exclusive access but don't wait if we can't lock it. |
| 94 | int err = flock(fd, LOCK_EX | LOCK_NB); |
| 95 | if (err < 0) { |
| 96 | PLOG(WARNING) << "Failed to lock profile file " << filename; |
| 97 | return -1; |
| 98 | } |
| 99 | |
| 100 | return fd; |
| 101 | } |
| 102 | |
| 103 | static bool CloseDescriptorForFile(int fd, const std::string& filename) { |
| 104 | // Now unlock the file, allowing another process in. |
| 105 | int err = flock(fd, LOCK_UN); |
| 106 | if (err < 0) { |
| 107 | PLOG(WARNING) << "Failed to unlock profile file " << filename; |
| 108 | return false; |
| 109 | } |
| 110 | |
| 111 | // Done, close the file. |
| 112 | err = ::close(fd); |
| 113 | if (err < 0) { |
| 114 | PLOG(WARNING) << "Failed to close descriptor for profile file" << filename; |
| 115 | return false; |
| 116 | } |
| 117 | |
| 118 | return true; |
| 119 | } |
| 120 | |
| 121 | static void WriteToFile(int fd, const std::ostringstream& os) { |
| 122 | std::string data(os.str()); |
| 123 | const char *p = data.c_str(); |
| 124 | size_t length = data.length(); |
| 125 | do { |
| 126 | int n = ::write(fd, p, length); |
| 127 | p += n; |
| 128 | length -= n; |
| 129 | } while (length > 0); |
| 130 | } |
| 131 | |
| 132 | static constexpr char kFieldSeparator = ','; |
| 133 | static constexpr char kLineSeparator = '\n'; |
| 134 | |
| 135 | /** |
| 136 | * Serialization format: |
| 137 | * multidex_suffix1,dex_location_checksum1,method_id11,method_id12... |
| 138 | * multidex_suffix2,dex_location_checksum2,method_id21,method_id22... |
| 139 | * e.g. |
| 140 | * ,131232145,11,23,454,54 -> this is the first dex file, it has no multidex suffix |
| 141 | * :classes5.dex,218490184,39,13,49,1 -> this is the fifth dex file. |
| 142 | **/ |
| 143 | bool OfflineProfilingInfo::Serialize(const std::string& filename, |
| 144 | const DexFileToMethodsMap& info) const { |
| 145 | int fd = OpenOrCreateFile(filename); |
| 146 | if (fd == -1) { |
| 147 | return false; |
| 148 | } |
| 149 | |
| 150 | // TODO(calin): Merge with a previous existing profile. |
| 151 | // TODO(calin): Profile this and see how much memory it takes. If too much, |
| 152 | // write to file directly. |
| 153 | std::ostringstream os; |
| 154 | for (auto it : info) { |
| 155 | const DexFile* dex_file = it.first; |
| 156 | const std::set<uint32_t>& method_dex_ids = it.second; |
| 157 | |
| 158 | os << DexFile::GetMultiDexSuffix(dex_file->GetLocation()) |
| 159 | << kFieldSeparator |
| 160 | << dex_file->GetLocationChecksum(); |
| 161 | for (auto method_it : method_dex_ids) { |
| 162 | os << kFieldSeparator << method_it; |
| 163 | } |
| 164 | os << kLineSeparator; |
| 165 | } |
| 166 | |
| 167 | WriteToFile(fd, os); |
| 168 | |
| 169 | return CloseDescriptorForFile(fd, filename); |
| 170 | } |
| 171 | } // namespace art |