Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2d1b9945 authored by Chris Manton's avatar Chris Manton
Browse files

test: log capture add fd sync

Bug: 244092860
Test: atest bluetooth_test_gd_unit bluetooth_test_gd
Tag: #refactor
BYPASS_LONG_LINES_REASON: Bluetooth likes 120 lines
Ignore-AOSP-First: Cherry-pick

Merged-In: I5c60363de865463a6a441e8c5e622713c25496b2
Change-Id: I5c60363de865463a6a441e8c5e622713c25496b2
parent 8299d71f
Loading
Loading
Loading
Loading
+168 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022 The Android Open Source Project
 *
 * 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 "common/testing/log_capture.h"

#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>

#include <cstddef>
#include <sstream>
#include <string>

#include "os/log.h"

namespace {
constexpr char kTempFilename[] = "/tmp/bt_gtest_log_capture-XXXXXX";
constexpr size_t kTempFilenameMaxSize = 64;
constexpr size_t kBufferSize = 4096;
constexpr int kStandardErrorFd = STDERR_FILENO;
}  // namespace

namespace bluetooth {
namespace testing {

LogCapture::LogCapture() {
  fd_ = create_backing_store();
  if (fd_ == -1) {
    LOG_ERROR("Unable to create backing storage : %s", strerror(errno));
    return;
  }
  if (!set_non_blocking(fd_)) {
    LOG_ERROR("Unable to set socket non-blocking : %s", strerror(errno));
    return;
  }
  original_stderr_fd_ = fcntl(kStandardErrorFd, F_DUPFD_CLOEXEC);
  if (original_stderr_fd_ == -1) {
    LOG_ERROR("Unable to save original fd : %s", strerror(errno));
    return;
  }
  if (dup3(fd_, kStandardErrorFd, O_CLOEXEC) == -1) {
    LOG_ERROR("Unable to duplicate stderr fd : %s", strerror(errno));
    return;
  }
}

LogCapture::~LogCapture() {
  Rewind()->Flush();
  clean_up();
}

LogCapture* LogCapture::Rewind() {
  if (fd_ != -1) {
    if (lseek(fd_, 0, SEEK_SET) != 0) {
      LOG_ERROR("Unable to rewind log capture : %s", strerror(errno));
    }
  }
  return this;
}

bool LogCapture::Find(std::string to_find) {
  std::string str = this->Read();
  return str.find(to_find) != std::string::npos;
}

void LogCapture::Flush() {
  if (fd_ != -1 && original_stderr_fd_ != -1) {
    ssize_t sz{-1};
    do {
      char buf[kBufferSize];
      sz = read(fd_, buf, sizeof(buf));
      if (sz > 0) {
        write(original_stderr_fd_, buf, sz);
      }
    } while (sz == kBufferSize);
  }
}

void LogCapture::Sync() {
  if (fd_ != -1) {
    fsync(fd_);
  }
}

void LogCapture::Reset() {
  if (fd_ != -1) {
    if (ftruncate(fd_, 0UL) == -1) {
      LOG_ERROR("Unable to truncate backing storage : %s", strerror(errno));
    }
    this->Rewind();
  }
}

std::string LogCapture::Read() {
  if (fd_ == -1) {
    return std::string();
  }
  std::ostringstream oss;
  ssize_t sz{-1};
  do {
    char buf[kBufferSize];
    sz = read(fd_, buf, sizeof(buf));
    if (sz > 0) {
      oss << buf;
    }
  } while (sz == kBufferSize);
  return oss.str();
}

size_t LogCapture::Size() const {
  size_t size{0UL};
  struct stat statbuf;
  if (fd_ != -1 && fstat(fd_, &statbuf) != -1) {
    size = statbuf.st_size;
  }
  return size;
}

int LogCapture::create_backing_store() const {
  char backing_store_filename[kTempFilenameMaxSize];
  strncpy(backing_store_filename, kTempFilename, kTempFilenameMaxSize);
  int fd = mkstemp(backing_store_filename);
  if (fd != -1) {
    unlink(backing_store_filename);
  }
  return fd;
}

bool LogCapture::set_non_blocking(int fd) const {
  int flags = fcntl(fd, F_GETFL, 0);
  if (flags == -1) {
    LOG_ERROR("Unable to get file descriptor flags : %s", strerror(errno));
    return false;
  }
  if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
    LOG_ERROR("Unable to set file descriptor flags : %s", strerror(errno));
    return false;
  }
  return true;
}

void LogCapture::clean_up() {
  if (original_stderr_fd_ != -1) {
    if (dup3(original_stderr_fd_, kStandardErrorFd, O_CLOEXEC) != kStandardErrorFd) {
      LOG_ERROR("Unable to restore original fd : %s", strerror(errno));
    }
  }
  if (fd_ != -1) {
    close(fd_);
    fd_ = -1;
  }
}

}  // namespace testing
}  // namespace bluetooth
+55 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022 The Android Open Source Project
 *
 * 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 <cstddef>
#include <string>

namespace bluetooth {
namespace testing {

class LogCapture {
 public:
  LogCapture();
  ~LogCapture();

  // Rewind file pointer to start of log
  // Returns a |this| pointer for chaining.  See |Find|
  LogCapture* Rewind();
  // Searches from filepointer to end of file for |to_find| string
  // Returns true if found, false otherwise
  bool Find(std::string to_find);
  // Reads and returns the entirety of the backing store into a string
  std::string Read();
  // Flushes contents of log capture back to |stderr|
  void Flush();
  // Synchronize buffer contents to file descriptor
  void Sync();
  // Returns the backing store size in bytes
  size_t Size() const;
  // Truncates and resets the file pointer discarding all logs up to this point
  void Reset();

 private:
  int create_backing_store() const;
  bool set_non_blocking(int fd) const;
  void clean_up();

  int fd_{-1};
  int original_stderr_fd_{-1};
};

}  // namespace testing
}  // namespace bluetooth