blob: ed20a773f54b98ab128b89887a4bb6f62cd7e2f9 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <errno.h>
#include <fcntl.h>
#include <fuchsia/hardware/block/c/fidl.h>
#include <fuchsia/io/c/fidl.h>
#include <fuchsia/minfs/c/fidl.h>
#include <fuchsia/minfs/llcpp/fidl.h>
#include <fuchsia/storage/metrics/c/fidl.h>
#include <getopt.h>
#include <lib/fdio/cpp/caller.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <zircon/device/block.h>
#include <zircon/types.h>
#include <utility>
#include <fbl/string_buffer.h>
#include <fbl/unique_fd.h>
#include <storage-metrics/block-metrics.h>
#include <storage-metrics/fs-metrics.h>
#include "src/storage/minfs/metrics.h"
namespace {
using MinfsFidlMetrics = ::llcpp::fuchsia::minfs::Metrics;
int Usage() {
fprintf(stdout, "usage: storage-metrics [ <option>* ] [paths]\n");
fprintf(stdout,
" storage-metrics reports metrics for storage components (block"
" devices and filesystems). It is currently limited to minfs\n");
fprintf(stdout, " --clear : clears metrics on block devices supporting paths\n");
fprintf(stdout,
" --enable_metrics=[true|false] : enables or disables metrics"
" for the filesystems supporting path\n");
fprintf(stdout, " --help : Show this help message\n");
return -1;
}
// Type to track whether whether a boolean flag without a default value has been set
enum class BooleanFlagState { kUnset, kEnable, kDisable };
struct StorageMetricOptions {
// True indicates that a call to retrieve block device metrics should also clear those metrics.
bool clear_block = false;
// Value passed to a filesystem toggle metrics request.
BooleanFlagState enable_fs_metrics = BooleanFlagState::kUnset;
};
void PrintFsMetrics(const MinfsFidlMetrics& metrics, const char* path) {
minfs::MinfsMetrics minfs_metrics(&metrics);
printf("Filesystem Metrics for: %s\n", path);
printf("General IO metrics\n");
minfs_metrics.Dump(stdout, true);
minfs_metrics.Dump(stdout, false);
// minfs_metrics.Dump(stdout);
printf("\n");
}
// Sends a FIDL call to enable or disable filesystem metrics for path
zx_status_t EnableFsMetrics(const char* path, bool enable) {
fbl::unique_fd fd(open(path, O_RDONLY | O_DIRECTORY));
if (!fd) {
fprintf(stderr, "Error opening %s, errno %d (%s)\n", path, errno, strerror(errno));
return ZX_ERR_IO;
}
zx_status_t status;
fdio_cpp::FdioCaller caller(std::move(fd));
zx_status_t rc = fuchsia_minfs_MinfsToggleMetrics(caller.borrow_channel(), enable, &status);
if (rc != ZX_OK || status != ZX_OK) {
fprintf(stderr, "Error toggling metrics for %s, status %d\n", path,
(rc != ZX_OK) ? rc : status);
return (rc != ZX_OK) ? rc : status;
}
return status;
}
// Retrieves the Filesystem metrics for path. Only supports Minfs.
zx_status_t GetFsMetrics(const char* path, MinfsFidlMetrics* out_metrics) {
fbl::unique_fd fd(open(path, O_RDONLY | O_DIRECTORY));
if (!fd) {
fprintf(stderr, "Error opening %s, errno %d (%s)\n", path, errno, strerror(errno));
return ZX_ERR_IO;
}
fdio_cpp::FdioCaller caller(std::move(fd));
auto result = llcpp::fuchsia::minfs::Minfs::Call::GetMetrics(caller.channel());
if (!result.ok()) {
fprintf(stderr, "Error getting metrics for %s, status %d\n", path, result.status());
return result.status();
}
if (result.value().status == ZX_ERR_UNAVAILABLE) {
fprintf(stderr, "Metrics Unavailable for %s\n", path);
return result.status();
}
if (result.value().status != ZX_OK) {
fprintf(stderr, "Error getting metrics for %s, status %d\n", path, result.value().status);
return result.value().status;
}
if (!result.value().metrics) {
fprintf(stderr, "Error getting metrics for %s, returned metrics was null\n", path);
return ZX_ERR_INTERNAL;
}
*out_metrics = *result.value().metrics;
return ZX_OK;
}
void PrintBlockMetrics(const char* dev, const fuchsia_hardware_block_BlockStats& stats) {
printf("Block Metrics for device path: %s\n", dev);
storage_metrics::BlockDeviceMetrics metrics(&stats);
metrics.Dump(stdout);
}
// Retrieves metrics for the block device at dev. Clears metrics if clear is true.
zx_status_t GetBlockMetrics(const char* dev, bool clear, fuchsia_hardware_block_BlockStats* stats) {
fbl::unique_fd fd(open(dev, O_RDONLY));
if (!fd) {
fprintf(stderr, "Error opening %s, errno %d (%s)\n", dev, errno, strerror(errno));
return ZX_ERR_IO;
}
fdio_cpp::FdioCaller caller(std::move(fd));
zx_status_t status;
zx_status_t io_status =
fuchsia_hardware_block_BlockGetStats(caller.borrow_channel(), clear, &status, stats);
if (io_status != ZX_OK) {
status = io_status;
}
if (status != ZX_OK) {
fprintf(stderr, "Error getting stats for %s\n", dev);
return status;
}
return ZX_OK;
}
void ParseCommandLineArguments(int argc, char** argv, StorageMetricOptions* options) {
static const struct option opts[] = {
{"clear", optional_argument, NULL, 'c'},
{"enable_metrics", optional_argument, NULL, 'e'},
{"help", no_argument, NULL, 'h'},
{0, 0, 0, 0},
};
for (int opt; (opt = getopt_long(argc, argv, "c::e::h", opts, nullptr)) != -1;) {
switch (opt) {
case 'c':
options->clear_block = (optarg == nullptr || strcmp(optarg, "true") == 0);
break;
case 'e':
options->enable_fs_metrics = (optarg == nullptr || strcmp(optarg, "true") == 0)
? BooleanFlagState::kEnable
: BooleanFlagState::kDisable;
break;
case 'h':
__FALLTHROUGH;
default:
Usage();
}
}
}
// Retrieves filesystem metrics for the filesystem at path and prints them.
void RunFsMetrics(const fbl::StringBuffer<PATH_MAX> path, const StorageMetricOptions options) {
fbl::unique_fd fd(open(path.c_str(), O_RDONLY | O_ADMIN));
if (!fd) {
fd.reset(open(path.c_str(), O_RDONLY));
if (!fd) {
fprintf(stderr, "storage-metrics could not open target: %s, errno %d (%s)\n", path.c_str(),
errno, strerror(errno));
return;
}
}
fuchsia_io_FilesystemInfo info;
zx_status_t status;
fdio_cpp::FdioCaller caller(std::move(fd));
zx_status_t io_status =
fuchsia_io_DirectoryAdminQueryFilesystem(caller.borrow_channel(), &status, &info);
if (io_status != ZX_OK || status != ZX_OK) {
fprintf(stderr, "storage-metrics could not open %s, status %d\n", path.c_str(),
(io_status != ZX_OK) ? io_status : status);
return;
}
// Skip any filesystems that aren't minfs
info.name[fuchsia_io_MAX_FS_NAME_BUFFER - 1] = '\0';
const char* name = reinterpret_cast<const char*>(info.name);
if (strcmp(name, "minfs") != 0) {
fprintf(stderr, "storage-metrics does not support filesystem type %s\n", name);
return;
}
zx_status_t rc;
// The order of these conditionals allows for stats to be output regardless of the
// value of enable.
if (options.enable_fs_metrics == BooleanFlagState::kEnable) {
rc = EnableFsMetrics(path.c_str(), true);
if (rc != ZX_OK) {
fprintf(stderr,
"storage-metrics could not enable filesystem metrics for %s,"
" status %d\n",
path.c_str(), rc);
return;
}
}
MinfsFidlMetrics metrics;
rc = GetFsMetrics(path.c_str(), &metrics);
if (rc == ZX_OK) {
PrintFsMetrics(metrics, path.c_str());
} else {
fprintf(stderr,
"storage-metrics could not get filesystem metrics for %s,"
" status %d\n",
path.c_str(), rc);
return;
}
if (options.enable_fs_metrics == BooleanFlagState::kDisable) {
rc = EnableFsMetrics(path.c_str(), false);
if (rc != ZX_OK) {
fprintf(stderr,
"storage-metrics could not disable filesystem metrics for %s,"
" status %d\n",
path.c_str(), rc);
}
}
}
// Retrieves and prints metrics for the block device associated with the filesystem at path.
void RunBlockMetrics(const fbl::StringBuffer<PATH_MAX> path, const StorageMetricOptions options) {
fbl::unique_fd fd(open(path.c_str(), O_RDONLY | O_ADMIN));
if (!fd) {
fd.reset(open(path.c_str(), O_RDONLY));
if (!fd) {
fprintf(stderr, "storage-metrics could not open target: %s, errno %d (%s)\n", path.c_str(),
errno, strerror(errno));
return;
}
}
char device_buffer[1024];
size_t path_len;
zx_status_t status;
fdio_cpp::FdioCaller caller(std::move(fd));
zx_status_t io_status = fuchsia_io_DirectoryAdminGetDevicePath(
caller.borrow_channel(), &status, device_buffer, sizeof(device_buffer) - 1, &path_len);
const char* device_path = nullptr;
if (io_status == ZX_OK && status == ZX_OK) {
device_buffer[path_len] = '\0';
device_path = device_buffer;
}
zx_status_t rc;
fuchsia_hardware_block_BlockStats stats;
if (device_path != nullptr) {
rc = GetBlockMetrics(device_path, options.clear_block, &stats);
if (rc == ZX_OK) {
PrintBlockMetrics(device_path, stats);
} else {
fprintf(stderr,
"storage-metrics could not retrieve block metrics for %s,"
" status %d\n",
path.c_str(), rc);
}
} else {
// Maybe this is not a filesystem. See if this happens to be a block device.
// TODO(auradkar): We need better args parsing to consider fs and block
// device seperately.
rc = GetBlockMetrics(path.c_str(), options.clear_block, &stats);
if (rc != ZX_OK) {
fprintf(stderr,
"storage-metrics could not retrieve block metrics for %s,"
" status %d\n",
path.c_str(), rc);
}
PrintBlockMetrics(path.c_str(), stats);
}
}
} // namespace
int main(int argc, char** argv) {
StorageMetricOptions options;
ParseCommandLineArguments(argc, argv, &options);
// Iterate through the remaining arguments, which are all paths
for (int i = optind; i < argc; i++) {
fbl::StringBuffer<PATH_MAX> path;
path.Append(argv[i]);
printf("Metrics for: %s\n", path.c_str());
RunFsMetrics(path, options);
RunBlockMetrics(path, options);
printf("\n");
}
return 0;
}