offline_profiling_info.cc revision 31f2c155975c5794d481df03eb0947cb48d2c6b5
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
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
172