blob: 5e4ce256e3172ef49bec14a976e0a16bd4d60924 [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 <fidl/fuchsia.fshost/cpp/wire.h>
#include <fidl/fuchsia.hardware.block/cpp/wire.h>
#include <fidl/fuchsia.io/cpp/wire.h>
#include <getopt.h>
#include <lib/fdio/cpp/caller.h>
#include <lib/service/llcpp/service.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <zircon/device/block.h>
#include <zircon/types.h>
#include <string>
#include <utility>
#include <fbl/unique_fd.h>
#include <storage-metrics/block-metrics.h>
#include "src/storage/fshost/constants.h"
namespace {
namespace fio = fuchsia_io;
int Usage() {
fprintf(stdout, "usage: storage-metrics [ <option>* ] [paths]\n");
fprintf(stdout, "storage-metrics reports metrics for block devices\n");
fprintf(stdout, " --clear : clears metrics on block devices supporting paths\n");
fprintf(stdout, " --help : Show this help message\n");
return -1;
}
struct StorageMetricOptions {
// True indicates that a call to retrieve block device metrics should also clear those metrics.
bool clear_block = false;
};
void PrintBlockMetrics(const char* dev, const fuchsia_hardware_block::wire::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<fuchsia_hardware_block::wire::BlockStats> GetBlockStats(const char* dev, bool clear) {
auto client_end = service::Connect<fuchsia_hardware_block::Block>(dev);
if (!client_end.is_ok()) {
return client_end.take_error();
}
fidl::WireSyncClient client(std::move(client_end.value()));
auto result = client->GetStats(clear);
zx_status_t status = !result.ok() ? result.error().status() : result.value().status;
if (status != ZX_OK) {
fprintf(stderr, "Error getting stats for %s: %s\n", dev, zx_status_get_string(status));
return zx::error(status);
}
return zx::ok(*result.value().stats);
}
void ParseCommandLineArguments(int argc, char** argv, StorageMetricOptions* options) {
static const struct option opts[] = {
{"clear", optional_argument, NULL, 'c'},
{"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 'h':
__FALLTHROUGH;
default:
Usage();
}
}
}
// Retrieves and prints metrics for the block device associated with the filesystem at path.
void RunBlockMetrics(const char* path, const StorageMetricOptions options) {
fbl::unique_fd fd(open(path, O_RDONLY));
if (!fd) {
fd.reset(open(path, O_RDONLY));
if (!fd) {
fprintf(stderr, "storage-metrics could not open target: %s, errno %d (%s)\n", path, errno,
strerror(errno));
return;
}
}
std::string device_path;
fdio_cpp::FdioCaller caller(std::move(fd));
auto result = fidl::WireCall(caller.directory())->QueryFilesystem();
if (result.ok() && result.value().s == ZX_OK) {
std::string fshost_path(fshost::kHubAdminServicePath);
auto fshost_or = service::Connect<fuchsia_fshost::Admin>(fshost_path.c_str());
if (fshost_or.is_error()) {
fprintf(stderr, "Error connecting to fshost (@ %s): %s\n", fshost_path.c_str(),
fshost_or.status_string());
} else {
auto path_result = fidl::WireCall(*fshost_or)->GetDevicePath(result.value().info->fs_id);
if (path_result.ok() && path_result->is_ok()) {
device_path = std::string(path_result->value()->path.get());
}
}
}
if (!device_path.empty()) {
zx::status<fuchsia_hardware_block::wire::BlockStats> block_stats =
GetBlockStats(device_path.data(), options.clear_block);
if (block_stats.is_ok()) {
PrintBlockMetrics(device_path.data(), *block_stats);
} else {
fprintf(stderr, "storage-metrics could not retrieve block metrics for %s: %s\n", path,
block_stats.status_string());
}
} 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.
zx::status<fuchsia_hardware_block::wire::BlockStats> block_stats =
GetBlockStats(path, options.clear_block);
if (!block_stats.is_ok()) {
fprintf(stderr, "storage-metrics could not retrieve block metrics for %s: %s\n", path,
block_stats.status_string());
} else {
PrintBlockMetrics(path, *block_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++) {
char* path = argv[i];
printf("Metrics for: %s\n", path);
RunBlockMetrics(path, options);
printf("\n");
}
return 0;
}