blob: 4450653a904cd987f5779fbc332c5fc1c108c1c1 [file] [log] [blame]
Calin Juravle31f2c152015-10-23 17:56:15 +01001/*
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
31namespace art {
32
33// An arbitrary value to throttle save requests. Set to 500ms for now.
34static constexpr const uint64_t kMilisecondsToNano = 1000000;
35static constexpr const uint64_t kMinimumTimeBetweenSavesNs = 500 * kMilisecondsToNano;
36
37bool OfflineProfilingInfo::NeedsSaving(uint64_t last_update_time_ns) const {
38 return last_update_time_ns - last_update_time_ns_.LoadRelaxed() > kMinimumTimeBetweenSavesNs;
39}
40
41void 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
72void 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
83static 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
103static 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
121static 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
132static constexpr char kFieldSeparator = ',';
133static 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 **/
143bool 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