• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2019 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "session.h"
16 
17 #include "prefetcher/prefetcher_daemon.h"
18 #include "prefetcher/task_id.h"
19 #include "serialize/arena_ptr.h"
20 #include "serialize/protobuf_io.h"
21 
22 #include <android-base/logging.h>
23 #include <android-base/properties.h>
24 #include <android-base/unique_fd.h>
25 #include <fcntl.h>
26 #include <functional>
27 #include <stdint.h>
28 #include <sys/mman.h>
29 #include <sys/time.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <unordered_map>
33 
34 namespace iorap {
35 namespace prefetcher {
36 
37 // Print per-entry details even if successful. Default-off, too spammy.
38 static constexpr bool kLogVerboseReadAhead = false;
39 
operator <<(std::ostream & os,const Session & session)40 std::ostream& operator<<(std::ostream& os, const Session& session) {
41   session.Dump(os, /*multiline*/false);
42   return os;
43 }
44 
Session()45 Session::Session() {
46 }
47 
Dump(std::ostream & os,bool multiline) const48 void SessionBase::Dump(std::ostream& os, bool multiline) const {
49   if (!multiline) {
50     os << "Session{";
51     os << "session_id=" << SessionId();
52     os << "}";
53     return;
54   } else {
55     os << "Session (id=" << SessionId() << ")" << std::endl;
56     return;
57   }
58 }
59 
SessionBase(size_t session_id,std::string description)60 SessionBase::SessionBase(size_t session_id, std::string description)
61   : session_id_{session_id}, description_{description} {
62 }
63 
GetFilePath(size_t path_id) const64 std::optional<std::string_view> SessionBase::GetFilePath(size_t path_id) const {
65   auto it = path_map_.find(path_id);
66   if (it != path_map_.end()) {
67     return {it->second};
68   } else {
69     return std::nullopt;
70   }
71 }
72 
RemoveFilePath(size_t path_id)73 bool SessionBase::RemoveFilePath(size_t path_id) {
74   auto it = path_map_.find(path_id);
75   if (it != path_map_.end()) {
76     path_map_.erase(it);
77     return true;
78   } else {
79     return false;
80   }
81 }
82 
InsertFilePath(size_t path_id,std::string file_path)83 bool SessionBase::InsertFilePath(size_t path_id, std::string file_path) {
84   path_map_.insert({path_id, std::move(file_path)});
85   return true;
86 }
87 
ProcessFd(int fd)88 bool SessionBase::ProcessFd(int fd) {
89   // Only SessionDirect has an implementation of this.
90   // TODO: Maybe add a CommandChoice::kProcessFd ? instead of kCreateFdSession?
91   LOG(FATAL) << "SessionBase::ProcessFd is not implemented";
92 
93   return false;
94 }
95 
96 //
97 // Direct
98 //
99 
operator <<(std::ostream & os,const SessionDirect::Entry & entry)100 std::ostream& operator<<(std::ostream& os, const SessionDirect::Entry& entry) {
101   os << "Entry{";
102   os << "path_id=" << entry.path_id << ",";
103   os << "kind=" << static_cast<int>(entry.kind) << ",";
104   os << "length=" << entry.length << ",";
105   os << "offset=" << entry.offset << ",";
106   os << "}";
107 
108   return os;
109 }
110 
111 // Just in case the failures are slowing down performance, turn them off.
112 // static constexpr bool kLogFailures = true;
113 static constexpr bool kLogFailures = false;
114 
RegisterFilePath(size_t path_id,std::string_view file_path)115 bool SessionDirect::RegisterFilePath(size_t path_id, std::string_view file_path) {
116   std::string file_path_str{file_path};  // no c_str for string_view.
117 
118   auto fd = TEMP_FAILURE_RETRY(open(file_path_str.c_str(), O_RDONLY));
119   if (fd < 0) {
120     if (kLogFailures) {
121       PLOG(ERROR) << "Failed to register file path: " << file_path << ", id=" << path_id
122                   << ", open(2) failed: ";
123     }
124     fd = android::base::unique_fd{};  // mark as 'bad' descriptor.
125   }
126 
127   LOG(VERBOSE) << "RegisterFilePath path_id=" << path_id << ", file_path=" << file_path_str;
128 
129   if (!InsertFilePath(path_id, std::move(file_path_str))) {
130     return false;
131   }
132 
133   path_fd_map_.insert(std::make_pair(path_id, std::move(fd)));
134   DCHECK(entry_list_map_[path_id].empty());
135 
136   return true;
137 }
138 
139 
UnregisterFilePath(size_t path_id)140 bool SessionDirect::UnregisterFilePath(size_t path_id) {
141   if (!RemoveFilePath(path_id)) {
142     return false;
143   }
144 
145   {  // Scoped FD reference lifetime.
146     auto maybe_fd = GetFdForPath(path_id);
147 
148     DCHECK(*maybe_fd != nullptr);
149     const android::base::unique_fd& entry_fd = **maybe_fd;
150 
151     auto list = entry_list_map_[path_id];
152 
153     for (const EntryMapping& entry_mapping : list) {
154       ReadAheadKind kind = entry_mapping.entry.kind;
155 
156       switch (kind) {
157         case ReadAheadKind::kFadvise:
158           // Nothing to do.
159           break;
160         case ReadAheadKind::kMmapLocked:
161         FALLTHROUGH_INTENDED;
162         case ReadAheadKind::kMlock:
163           // Don't do any erases in the unregister file path to avoid paying O(n^2) erase cost.
164           UnmapWithoutErase(entry_mapping);
165           break;
166       }
167     }
168   }
169 
170   auto it = entry_list_map_.find(path_id);
171   auto end = entry_list_map_.end();
172   DCHECK(it != end);
173   entry_list_map_.erase(it);
174 
175   // Close the FD for this file path.
176   auto fd_it = path_fd_map_.find(path_id);
177   DCHECK(fd_it != path_fd_map_.end());
178   path_fd_map_.erase(fd_it);
179 
180   return true;
181 }
182 
183 // Note: return a pointer because optional doesn't hold references directly.
GetFdForPath(size_t path_id)184 std::optional<android::base::unique_fd*> SessionDirect::GetFdForPath(size_t path_id) {
185   auto it = path_fd_map_.find(path_id);
186   if (it == path_fd_map_.end()) {
187     return std::nullopt;
188   } else {
189     return &it->second;
190   }
191 }
192 
ReadAhead(size_t path_id,ReadAheadKind kind,size_t length,size_t offset)193 bool SessionDirect::ReadAhead(size_t path_id,
194                               ReadAheadKind kind,
195                               size_t length,
196                               size_t offset) {
197   // Take by-reference so we can mutate list at the end.
198   auto& list = entry_list_map_[path_id];
199 
200   Entry entry{path_id, kind, length, offset};
201   EntryMapping entry_mapping{entry, /*address*/nullptr, /*success*/false};
202 
203   bool success = true;
204 
205   auto maybe_fd = GetFdForPath(path_id);
206   if (!maybe_fd) {
207     LOG(ERROR) << "SessionDirect: Failed to find FD for path_id=" << path_id;
208     return false;
209   }
210 
211   DCHECK(*maybe_fd != nullptr);
212   const android::base::unique_fd& entry_fd = **maybe_fd;
213 
214   std::optional<std::string_view> file_name_opt = GetFilePath(path_id);
215   DCHECK(file_name_opt.has_value());  // if one map has it, all maps have it.
216   std::string_view file_name = *file_name_opt;
217 
218   if (!entry_fd.ok()) {
219     LOG(VERBOSE) << "SessionDirect: No file descriptor for (path_id=" << path_id << ") "
220                  << "path '" << file_name << "', failed to readahead entry.";
221     // Even failures get kept with success=false.
222     list.push_back(entry_mapping);
223     return false;
224   }
225 
226   switch (kind) {
227     case ReadAheadKind::kFadvise:
228       if (posix_fadvise(entry_fd, offset, length, POSIX_FADV_WILLNEED) < 0) {
229         PLOG(ERROR) << "SessionDirect: Failed to fadvise entry " << file_name
230                     << ", offset=" << offset << ", length=" << length;
231         success = false;
232       }
233       break;
234     case ReadAheadKind::kMmapLocked:
235     FALLTHROUGH_INTENDED;
236     case ReadAheadKind::kMlock: {
237       const bool need_mlock = kind == ReadAheadKind::kMlock;
238 
239       int flags = MAP_SHARED;
240       if (!need_mlock) {
241         // MAP_LOCKED is a best-effort to lock the page. it could still be
242         // paged in later at a fault.
243         flags |= MAP_LOCKED;
244       }
245 
246       entry_mapping.address =
247         mmap(/*addr*/nullptr, length, PROT_READ, flags, entry_fd, offset);
248 
249       if (entry_mapping.address == nullptr) {
250         PLOG(ERROR) << "SessionDirect: Failed to mmap entry " << file_name
251                     << ", offset=" << offset << ", length=" << length;
252         success = false;
253         break;
254       }
255 
256       // Strong guarantee that page will be locked if mlock returns successfully.
257       if (need_mlock && mlock(entry_mapping.address, length) < 0) {
258         PLOG(ERROR) << "SessionDirect: Failed to mlock entry " << file_name
259                     << ", offset=" << offset << ", length=" << length;
260         // We already have a mapping address, so we should add it to the list.
261         // However this didn't succeed 100% because the lock failed, so return false later.
262         success = false;
263       }
264     }
265   }
266 
267   // Keep track of success so we know in Dump() what the number of failed entry mappings were.
268   entry_mapping.success = success;
269 
270   // Keep track of this so that we can clean it up later in UnreadAhead.
271   list.push_back(entry_mapping);
272 
273   if (entry_mapping.success) {
274     if (kLogVerboseReadAhead) {
275       LOG(VERBOSE) << "SessionDirect: ReadAhead for " << entry_mapping.entry;
276     }
277   }  // else one of the errors above already did print.
278 
279   return success;
280 }
281 
UnreadAhead(size_t path_id,ReadAheadKind kind,size_t length,size_t offset)282 bool SessionDirect::UnreadAhead(size_t path_id,
283                                 ReadAheadKind kind,
284                                 size_t length,
285                                 size_t offset) {
286   Entry entry{path_id, kind, length, offset};
287 
288   auto list = entry_list_map_[path_id];
289   if (list.empty()) {
290     return false;
291   }
292 
293   std::optional<EntryMapping> entry_mapping;
294   size_t idx = 0;
295 
296   for (size_t i = 0; i < list.size(); ++i) {
297     if (entry == list[i].entry) {
298       entry_mapping = list[i];
299       idx = 0;
300       break;
301     }
302   }
303 
304   if (!entry_mapping) {
305     return false;
306   }
307 
308   switch (kind) {
309     case ReadAheadKind::kFadvise:
310       // Nothing to do.
311       // TODO: maybe fadvise(RANDOM)?
312       return true;
313     case ReadAheadKind::kMmapLocked:
314     FALLTHROUGH_INTENDED;
315     case ReadAheadKind::kMlock:
316       UnmapWithoutErase(*entry_mapping);
317       return true;
318   }
319 
320   list.erase(list.begin() + idx);
321 
322   // FDs close only with UnregisterFilePath.
323   return true;
324 }
325 
UnmapWithoutErase(const EntryMapping & entry_mapping)326 void SessionDirect::UnmapWithoutErase(const EntryMapping& entry_mapping) {
327   void* address = entry_mapping.address;
328   size_t length = entry_mapping.entry.length;
329 
330   // munmap also unlocks. Do not need explicit munlock.
331   if (munmap(address, length) < 0) {
332     PLOG(WARNING) << "ReadAhead (Finish): Failed to munmap address: "
333                   << address << ", length: " << length;
334   }
335 
336 }
337 
ProcessFd(int fd)338 bool SessionDirect::ProcessFd(int fd) {
339   // TODO: the path is advisory, but it would still be cleaner to pass it separately
340   const char* fd_path = SessionDescription().c_str();
341 
342   android::base::Timer open_timer{};
343   android::base::Timer total_timer{};
344 
345   serialize::ArenaPtr<serialize::proto::TraceFile> trace_file_ptr =
346       serialize::ProtobufIO::Open(fd, fd_path);
347 
348   if (trace_file_ptr == nullptr) {
349     LOG(ERROR) << "SessionDirect::ProcessFd failed, corrupted protobuf format? " << fd_path;
350     return false;
351   }
352 
353   // TODO: maybe make it part of a kProcessFd type of command?
354   ReadAheadKind kind = ReadAheadKind::kFadvise;
355 
356   // TODO: The "Task[Id]" should probably be the one owning the trace file.
357   // When the task is fully complete, the task can be deleted and the
358   // associated arenas can go with them.
359 
360   // TODO: we should probably have the file entries all be relative
361   // to the package path?
362 
363   // Open every file in the trace index.
364   const serialize::proto::TraceFileIndex& index = trace_file_ptr->index();
365 
366   size_t count_entries = 0;
367   for (const serialize::proto::TraceFileIndexEntry& index_entry : index.entries()) {
368     LOG(VERBOSE) << "ReadAhead: found file entry: " << index_entry.file_name();
369 
370     if (index_entry.id() < 0) {
371       LOG(WARNING) << "ReadAhead: Skip bad TraceFileIndexEntry, negative ID not allowed: "
372                    << index_entry.id();
373       continue;
374     }
375 
376     size_t path_id = index_entry.id();
377     const auto& path_file_name = index_entry.file_name();
378 
379     if (!this->RegisterFilePath(path_id, path_file_name)) {
380       LOG(WARNING) << "ReadAhead: Failed to register file path: " << path_file_name;
381       ++count_entries;
382     }
383   }
384   LOG(VERBOSE) << "ReadAhead: Registered " << count_entries << " file paths";
385   std::chrono::milliseconds open_duration_ms = open_timer.duration();
386 
387   LOG(DEBUG) << "ProcessFd: open+parsed headers in " << open_duration_ms.count() << "ms";
388 
389   // Go through every trace entry and readahead every (file,offset,len) tuple.
390   size_t entry_offset = 0;
391   const serialize::proto::TraceFileList& file_list = trace_file_ptr->list();
392   for (const serialize::proto::TraceFileEntry& file_entry : file_list.entries()) {
393     ++entry_offset;
394 
395     if (file_entry.file_length() < 0 || file_entry.file_offset() < 0) {
396       LOG(WARNING) << "ProcessFd entry negative file length or offset, illegal: "
397                    << "index_id=" << file_entry.index_id() << ", skipping";
398       continue;
399     }
400 
401     // Attempt to perform readahead. This can generate more warnings dynamically.
402     if (!this->ReadAhead(file_entry.index_id(),
403                          kind,
404                          file_entry.file_length(),
405                          file_entry.file_offset())) {
406       if (kLogFailures) {
407         LOG(WARNING) << "Failed readahead, bad file length/offset in entry @ "
408                      << (entry_offset - 1);
409       }
410     }
411   }
412 
413   std::chrono::milliseconds total_duration_ms = total_timer.duration();
414   LOG(DEBUG) << "ProcessFd: total duration " << total_duration_ms.count() << "ms";
415 
416   {
417     struct timeval now;
418     gettimeofday(&now, nullptr);
419 
420     uint64_t now_usec = (now.tv_sec * 1000000LL + now.tv_usec);
421     LOG(DEBUG) << "ProcessFd: finishing usec: " << now_usec;
422   }
423 
424   return true;
425 }
426 
427 
IsDumpEveryEntry()428 static bool IsDumpEveryEntry() {
429   // Set to 'true' to dump every single entry for debugging (multiline).
430   // Otherwise it only prints per-file-path summaries.
431   return ::android::base::GetBoolProperty("iorapd.readahead.dump_all", /*default*/false);
432 }
433 
IsDumpEveryPath()434 static bool IsDumpEveryPath() {
435   // Dump per-file-path (entry) stats. Defaults to on if the above property is on.
436   return ::android::base::GetBoolProperty("iorapd.readahead.dump_paths", /*default*/false);
437 }
438 
Dump(std::ostream & os,bool multiline) const439 void SessionDirect::Dump(std::ostream& os, bool multiline) const {
440   {
441     struct timeval now;
442     gettimeofday(&now, nullptr);
443 
444     uint64_t now_usec = (now.tv_sec * 1000000LL + now.tv_usec);
445     LOG(DEBUG) << "SessionDirect::Dump: beginning usec: " << now_usec;
446   }
447 
448   size_t path_count = entry_list_map_.size();
449 
450   size_t read_ahead_entries = 0;
451   size_t read_ahead_bytes = 0;
452 
453   size_t overall_entry_count = 0;
454   size_t overall_byte_count = 0;
455   for (auto it = entry_list_map_.begin(); it != entry_list_map_.end(); ++it) {
456     const auto& entry_mapping_list = it->second;
457 
458     for (size_t j = 0; j < entry_mapping_list.size(); ++j) {
459       const EntryMapping& entry_mapping = entry_mapping_list[j];
460       const Entry& entry = entry_mapping.entry;
461 
462       ++overall_entry_count;
463       overall_byte_count += entry.length;
464 
465       if (entry_mapping.success) {
466         ++read_ahead_entries;
467         read_ahead_bytes += entry.length;
468       }
469     }
470   }
471 
472   double overall_success_entry_rate =
473       read_ahead_entries * 100.0 / overall_entry_count;
474   double overall_success_byte_rate =
475       read_ahead_bytes * 100.0 / overall_byte_count;
476 
477   size_t fd_count = path_fd_map_.size();
478   size_t good_fd_count = 0;
479   for (auto it = path_fd_map_.begin(); it != path_fd_map_.end(); ++it) {
480     if (it->second.ok()) {
481       ++good_fd_count;
482     }
483   }
484   double good_fd_rate = good_fd_count * 100.0 / fd_count;
485   // double bad_fd_rate = (fd_count - good_fd_count) * 1.0 / fd_count;
486 
487   if (!multiline) {
488     os << "SessionDirect{";
489     os << "session_id=" << SessionId() << ",";
490 
491     os << "file_paths=" << path_count << " (good: " << good_fd_rate << "),";
492     os << "read_ahead_entries=" << read_ahead_entries;
493     os << "(" << overall_success_entry_rate << "%),";
494     os << "read_ahead_bytes=" << read_ahead_bytes << "";
495     os << "(" << overall_success_byte_rate << "%),";
496     os << "timer=" << timer_.duration().count() << ",";
497 
498     os << "}";
499     return;
500   } else {
501     // Always try to pay attention to these stats below.
502     // They can be signs of potential performance problems.
503     os << "Session Direct (id=" << SessionId() << ")" << std::endl;
504 
505     os << "  Summary: " << std::endl;
506     os << "    Description = " << SessionDescription() << std::endl;
507     os << "    Duration = " << timer_.duration().count() << "ms" << std::endl;
508     os << "    Total File Paths=" << path_count << " (good: " << good_fd_rate << "%)" << std::endl;
509     os << "    Total Entries=" << overall_entry_count;
510     os << " (good: " << overall_success_entry_rate << "%)" << std::endl;
511     os << "    Total Bytes=" << overall_byte_count << "";
512     os << " (good: " << overall_success_byte_rate << "%)" << std::endl;
513     os << std::endl;
514 
515     // Probably too spammy, but they could narrow down the issue for a problem in above stats.
516     if (!IsDumpEveryPath() && !IsDumpEveryEntry()) {
517       return;
518     }
519 
520     for (auto it = entry_list_map_.begin(); it != entry_list_map_.end(); ++it) {
521       size_t path_id = it->first;
522       const auto& entry_mapping_list = it->second;
523 
524       std::optional<std::string_view> file_path = GetFilePath(path_id);
525       os << "  File Path (id=" << path_id << "): ";
526       if (file_path.has_value()) {
527         os << "'" << *file_path << "'";
528       } else {
529         os << "(nullopt)";
530       }
531 
532       auto fd_it = path_fd_map_.find(path_id);
533       os << ", FD=";
534       if (fd_it != path_fd_map_.end()) {
535         const android::base::unique_fd& fd = fd_it->second;
536         os << fd.get();  // -1 for failed fd.
537       } else {
538         os << "(none)";
539       }
540       os << std::endl;
541 
542       size_t total_entries = entry_mapping_list.size();
543       size_t total_bytes = 0;
544 
545       size_t local_read_ahead_entries = 0;
546       size_t local_read_ahead_bytes = 0;
547       for (size_t j = 0; j < entry_mapping_list.size(); ++j) {
548         const EntryMapping& entry_mapping = entry_mapping_list[j];
549         const Entry& entry = entry_mapping.entry;
550 
551         total_bytes += entry.length;
552 
553         // Sidenote: Bad FDs will have 100% failed mappings.
554         // Good FDs may sometimes have failed mappings.
555         if (entry_mapping.success) {
556           ++local_read_ahead_entries;
557           local_read_ahead_bytes += entry.length;
558         }
559 
560         if (IsDumpEveryEntry()) {
561           os << "    Entry " << j << " details:" << std::endl;
562           os << "      " << entry << std::endl;
563           os << "      Mapping " << (entry_mapping.success ? "Succeeded" :  "Failed")
564              << ", Address " << entry_mapping.address << std::endl;
565         }
566       }
567 
568       double entry_success_rate = local_read_ahead_entries * 100.0 / total_entries;
569       double bytes_success_rate = local_read_ahead_bytes * 100.0 / total_bytes;
570 
571       double entry_failure_rate = (total_entries - local_read_ahead_entries) * 100.0 / total_entries;
572       double bytes_failure_rate = (total_bytes - local_read_ahead_bytes) * 100.0 / total_bytes;
573 
574       os << "    Successful: Entries=" << local_read_ahead_entries
575          << " (" << entry_success_rate << "%)"
576          << ", Bytes=" << local_read_ahead_bytes
577          << " (" << bytes_success_rate << "%)"
578          << std::endl;
579       os << "    Failed: Entries=" << (total_entries - local_read_ahead_entries)
580          << " (" << entry_failure_rate << "%)"
581          << ", Bytes=" << (total_bytes - local_read_ahead_bytes)
582          << " (" << bytes_failure_rate << "%)"
583          << std::endl;
584       os << "    Total: Entries=" << total_entries
585          << ", Bytes=" << total_bytes
586          << std::endl;
587     }
588 
589     return;
590   }
591 }
592 
~SessionDirect()593 SessionDirect::~SessionDirect() {
594   for (auto it = entry_list_map_.begin(); it != entry_list_map_.end();) {
595     size_t path_id = it->first;
596 
597     ++it; // the iterator is removed in the following Unregister method.
598     UnregisterFilePath(path_id);
599   }
600 }
601 
602 //
603 // Indirect
604 //
605 
SessionIndirect(size_t session_id,std::string description,std::shared_ptr<PrefetcherDaemon> daemon,bool send_command)606 SessionIndirect::SessionIndirect(size_t session_id,
607                                  std::string description,
608                                  std::shared_ptr<PrefetcherDaemon> daemon,
609                                  bool send_command)
610     : SessionBase{session_id, description},
611       daemon_{daemon} {
612   // Don't do anything in e.g. subclasses.
613   if (!send_command) {
614     return;
615   }
616 
617   Command cmd{};
618   cmd.choice = CommandChoice::kCreateSession;
619   cmd.session_id = session_id;
620   cmd.file_path = description;
621 
622   LOG(VERBOSE) << "SessionIndirect: " << cmd;
623 
624   if (!daemon_->SendCommand(cmd)) {
625     LOG(FATAL) << "SessionIndirect: Failure to create session " << session_id
626                << ", description: " << description;
627   }
628 }
629 
~SessionIndirect()630 SessionIndirect::~SessionIndirect() {
631   Command cmd{};
632   cmd.choice = CommandChoice::kDestroySession;
633   cmd.session_id = SessionId();
634 
635   if (!daemon_->SendCommand(cmd)) {
636     LOG(WARNING) << "SessionIndirect: Failure to destroy session " << SessionId()
637                  << ", description: " << SessionDescription();
638   }
639 }
640 
Dump(std::ostream & os,bool multiline) const641 void SessionIndirect::Dump(std::ostream& os, bool multiline) const {
642   // SessionBase::Dump(os, multiline);
643   // TODO: does having the local dump do anything for us?
644 
645   Command cmd{};
646   cmd.choice = CommandChoice::kDumpSession;
647   cmd.session_id = SessionId();
648 
649   daemon_->SendCommand(cmd);
650 }
651 
RegisterFilePath(size_t path_id,std::string_view file_path)652 bool SessionIndirect::RegisterFilePath(size_t path_id, std::string_view file_path) {
653   Command cmd{};
654   cmd.choice = CommandChoice::kRegisterFilePath;
655   cmd.session_id = SessionId();
656   cmd.id = path_id;
657   cmd.file_path = file_path;
658 
659   return daemon_->SendCommand(cmd);
660 }
661 
UnregisterFilePath(size_t path_id)662 bool SessionIndirect::UnregisterFilePath(size_t path_id) {
663   Command cmd{};
664   cmd.choice = CommandChoice::kUnregisterFilePath;
665   cmd.session_id = SessionId();
666   cmd.id = path_id;
667 
668   return daemon_->SendCommand(cmd);
669 }
ReadAhead(size_t path_id,ReadAheadKind kind,size_t length,size_t offset)670 bool SessionIndirect::ReadAhead(size_t path_id,
671                                 ReadAheadKind kind,
672                                 size_t length,
673                                 size_t offset) {
674   Command cmd{};
675   cmd.choice = CommandChoice::kReadAhead;
676   cmd.session_id = SessionId();
677   cmd.id = path_id;
678   cmd.read_ahead_kind = kind;
679   cmd.length = length;
680   cmd.offset = offset;
681 
682   return daemon_->SendCommand(cmd);
683 }
684 
UnreadAhead(size_t path_id,ReadAheadKind kind,size_t length,size_t offset)685 bool SessionIndirect::UnreadAhead(size_t path_id,
686                                   ReadAheadKind kind,
687                                   size_t length,
688                                   size_t offset) {
689   LOG(WARNING) << "UnreadAhead: command not implemented yet";
690   return true;
691 }
692 
693 //
694 // IndirectSocket
695 //
696 
SessionIndirectSocket(size_t session_id,int fd,std::string description,std::shared_ptr<PrefetcherDaemon> daemon)697 SessionIndirectSocket::SessionIndirectSocket(size_t session_id,
698                                              int fd,
699                                              std::string description,
700                                              std::shared_ptr<PrefetcherDaemon> daemon)
701     : SessionIndirect{session_id, description, daemon, /*send_command*/false} {
702   // TODO: all of the WriteCommand etc in the daemon.
703   Command cmd{};
704   cmd.choice = CommandChoice::kCreateFdSession;
705   cmd.fd = fd;
706   cmd.session_id = session_id;
707   cmd.file_path = description;
708 
709   LOG(VERBOSE) << "SessionIndirectSocket: " << cmd;
710 
711   if (!daemon_->SendCommand(cmd)) {
712     LOG(FATAL) << "SessionIndirectSocket: Failure to create session " << session_id
713                << ", description: " << description;
714   }
715 
716   // This goes into the SessionDirect ctor + SessionDirect::ProcessFd
717   // as implemented in PrefetcherDaemon::ReceiveCommand
718 }
719 
~SessionIndirectSocket()720 SessionIndirectSocket::~SessionIndirectSocket() {
721 }
722 
723 }  // namespace prefetcher
724 }  // namespace iorap
725