[bugreport] --minimal to restrict the attachments to inspect.json
* if run from the target shell, the output of `bugreport` can be quite large
(logs, inspect, etc.). `bugreport --minimal` preserves the annotations and
the Inspect data, but skips the logs and build snapshot, which can be quite large.
* we add support for command-line option parsing and its own unit test
DX-1678 #done #comment
TESTED=`fx run-test bugreport_tests`
TESTED=`fx bugreport`
TESTED=`bugreport --minimal`
TESTED=`bugreport --help`
TESTED=`bugreport help`
Change-Id: Ibbdfd57fd6149286f218de0cdda87dc2c271369c
diff --git a/src/developer/bugreport/BUILD.gn b/src/developer/bugreport/BUILD.gn
index 5e41232..c6f6908d 100644
--- a/src/developer/bugreport/BUILD.gn
+++ b/src/developer/bugreport/BUILD.gn
@@ -34,11 +34,29 @@
]
deps = [
+ ":command_line_options",
":src",
"//sdk/lib/sys/cpp",
]
}
+source_set("bug_report_schema") {
+ sources = [
+ "bug_report_schema.h",
+ ]
+}
+
+source_set("command_line_options") {
+ sources = [
+ "command_line_options.cc",
+ "command_line_options.h",
+ ]
+
+ deps = [
+ "//src/lib/fxl",
+ ]
+}
+
source_set("src") {
sources = [
"bug_reporter.cc",
@@ -58,12 +76,6 @@
]
}
-source_set("bug_report_schema") {
- sources = [
- "bug_report_schema.h",
- ]
-}
-
# Host side client -------------------------------------------------------------
if (current_toolchain == host_toolchain) {
diff --git a/src/developer/bugreport/bug_reporter.cc b/src/developer/bugreport/bug_reporter.cc
index c907ea1..1c05d2b 100644
--- a/src/developer/bugreport/bug_reporter.cc
+++ b/src/developer/bugreport/bug_reporter.cc
@@ -10,6 +10,8 @@
#include <zircon/errors.h>
#include <zircon/status.h>
+#include <vector>
+
#include "src/developer/bugreport/bug_report_schema.h"
#include "third_party/rapidjson/include/rapidjson/document.h"
#include "third_party/rapidjson/include/rapidjson/filewritestream.h"
@@ -33,11 +35,16 @@
void AddAttachments(
const std::vector<fuchsia::feedback::Attachment>& attachments,
+ const std::set<std::string>& attachment_allowlist,
rapidjson::Document* document) {
rapidjson::Value json_attachments(rapidjson::kObjectType);
for (const auto& attachment : attachments) {
- // TODO(DX-1551): interpret the attachment value as a JSON object for
- // the "inspect" key.
+ if (!attachment_allowlist.empty() &&
+ attachment_allowlist.find(attachment.key) ==
+ attachment_allowlist.end()) {
+ continue;
+ }
+
std::string value;
if (!fsl::StringFromVmo(attachment.value, &value)) {
fprintf(stderr, "Failed to parse attachment VMO as string for key %s\n",
@@ -52,11 +59,12 @@
}
bool MakeAndWriteJson(const fuchsia::feedback::Data& feedback_data,
+ const std::set<std::string>& attachment_allowlist,
FILE* out_file) {
rapidjson::Document document;
document.SetObject();
AddAnnotations(feedback_data.annotations(), &document);
- AddAttachments(feedback_data.attachments(), &document);
+ AddAttachments(feedback_data.attachments(), attachment_allowlist, &document);
char buffer[65536];
rapidjson::FileWriteStream output_stream(out_file, buffer, sizeof(buffer));
@@ -73,6 +81,7 @@
} // namespace
bool MakeBugReport(std::shared_ptr<::sys::ServiceDirectory> services,
+ const std::set<std::string>& attachment_allowlist,
const char* out_filename) {
fuchsia::feedback::DataProviderSyncPtr feedback_data_provider;
services->Connect(feedback_data_provider.NewRequest());
@@ -99,11 +108,13 @@
fprintf(stderr, "Failed to open output file %s\n", out_filename);
return false;
}
- const bool success = MakeAndWriteJson(result.response().data, out_file);
+ const bool success = MakeAndWriteJson(result.response().data,
+ attachment_allowlist, out_file);
fclose(out_file);
return success;
} else {
- return MakeAndWriteJson(result.response().data, stdout);
+ return MakeAndWriteJson(result.response().data, attachment_allowlist,
+ stdout);
}
}
diff --git a/src/developer/bugreport/bug_reporter.h b/src/developer/bugreport/bug_reporter.h
index 7e8505f..46aad0b 100644
--- a/src/developer/bugreport/bug_reporter.h
+++ b/src/developer/bugreport/bug_reporter.h
@@ -8,16 +8,23 @@
#include <lib/sys/cpp/service_directory.h>
#include <memory>
+#include <set>
+#include <string>
namespace fuchsia {
namespace bugreport {
-// Makes a JSON file representing a bug report containing all the feedback data
-// by connecting to fuchsia.feedback.DataProvider from |services|.
+// Makes a JSON file representing a bug report containing the feedback data
+// (annotations and attachments) collected from fuchsia.feedback.DataProvider
+// from |services|.
//
-// By default, the JSON file is streamed to stdout. Use |out_filename| to output
-// it to a file.
+// By default:
+// - all the attachments are reported. Use |attachment_allowlist| to restrict
+// the attachments to specific keys, e.g., to minimize the output.
+// - the JSON file is streamed to stdout. Use |out_filename| to output it to
+// a file.
bool MakeBugReport(std::shared_ptr<::sys::ServiceDirectory> services,
+ const std::set<std::string>& attachment_allowlist = {},
const char* out_filename = nullptr);
} // namespace bugreport
diff --git a/src/developer/bugreport/command_line_options.cc b/src/developer/bugreport/command_line_options.cc
new file mode 100644
index 0000000..8019b36
--- /dev/null
+++ b/src/developer/bugreport/command_line_options.cc
@@ -0,0 +1,53 @@
+// Copyright 2019 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 "src/developer/bugreport/command_line_options.h"
+
+#include "src/lib/fxl/command_line.h"
+#include "src/lib/fxl/strings/substitute.h"
+
+namespace fuchsia {
+namespace bugreport {
+namespace {
+
+constexpr char kUsage[] = R"($0
+
+ Dumps in stdout a JSON file containing the feedback data (annotations and
+ attachments) collected from fuchsia.feedback.DataProvider.
+
+Usage:
+
+ $0 [--minimal]
+
+Arguments:
+
+ --minimal Restricts the attachments to the Inspect data only (no logs,
+ no build snapshot, etc.). Annotations are preserved.
+
+)";
+
+} // namespace
+
+Mode ParseModeFromArgcArgv(int argc, const char* const* argv) {
+ auto command_line = fxl::CommandLineFromArgcArgv(argc, argv);
+ auto& pos_args = command_line.positional_args();
+ if (command_line.HasOption("help") ||
+ (pos_args.size() == 1u && pos_args[0] == "help")) {
+ printf("%s\n", fxl::Substitute(kUsage, command_line.argv0()).c_str());
+ return Mode::HELP;
+ }
+
+ if (command_line.HasOption("minimal")) {
+ return Mode::MINIMAL;
+ } else if (!command_line.options().empty() || !pos_args.empty()) {
+ fprintf(stderr, "Unexpected option. Usage: %s\n",
+ fxl::Substitute(kUsage, command_line.argv0()).c_str());
+ return Mode::FAILURE;
+ }
+
+ return Mode::DEFAULT;
+}
+
+} // namespace bugreport
+} // namespace fuchsia
diff --git a/src/developer/bugreport/command_line_options.h b/src/developer/bugreport/command_line_options.h
new file mode 100644
index 0000000..700a099
--- /dev/null
+++ b/src/developer/bugreport/command_line_options.h
@@ -0,0 +1,23 @@
+// Copyright 2019 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.
+
+#ifndef SRC_DEVELOPER_BUGREPORT_COMMAND_LINE_OPTIONS_H_
+#define SRC_DEVELOPER_BUGREPORT_COMMAND_LINE_OPTIONS_H_
+
+namespace fuchsia {
+namespace bugreport {
+
+enum class Mode {
+ FAILURE,
+ HELP,
+ DEFAULT,
+ MINIMAL,
+};
+
+Mode ParseModeFromArgcArgv(int argc, const char* const* argv);
+
+} // namespace bugreport
+} // namespace fuchsia
+
+#endif // SRC_DEVELOPER_BUGREPORT_COMMAND_LINE_OPTIONS_H_
diff --git a/src/developer/bugreport/main.cc b/src/developer/bugreport/main.cc
index f4eb10a..574e153 100644
--- a/src/developer/bugreport/main.cc
+++ b/src/developer/bugreport/main.cc
@@ -3,13 +3,38 @@
// found in the LICENSE file.
#include <lib/sys/cpp/component_context.h>
+#include <stdio.h>
#include <stdlib.h>
+#include <set>
+#include <string>
+
#include "src/developer/bugreport/bug_reporter.h"
+#include "src/developer/bugreport/command_line_options.h"
+
+namespace {
+
+using fuchsia::bugreport::Mode;
+
+} // namespace
int main(int argc, char** argv) {
- auto environment_services = ::sys::ServiceDirectory::CreateFromNamespace();
+ const Mode mode = fuchsia::bugreport::ParseModeFromArgcArgv(argc, argv);
+ std::set<std::string> attachment_allowlist;
+ switch (mode) {
+ case Mode::FAILURE:
+ return EXIT_FAILURE;
+ case Mode::HELP:
+ return EXIT_SUCCESS;
+ case Mode::MINIMAL:
+ attachment_allowlist = {"inspect.json"};
+ case Mode::DEFAULT:
+ auto environment_services =
+ ::sys::ServiceDirectory::CreateFromNamespace();
- return fuchsia::bugreport::MakeBugReport(environment_services) ? EXIT_SUCCESS
- : EXIT_FAILURE;
+ return fuchsia::bugreport::MakeBugReport(environment_services,
+ attachment_allowlist)
+ ? EXIT_SUCCESS
+ : EXIT_FAILURE;
+ }
}
diff --git a/src/developer/bugreport/tests/BUILD.gn b/src/developer/bugreport/tests/BUILD.gn
index c291ad7..8c3dbd4 100644
--- a/src/developer/bugreport/tests/BUILD.gn
+++ b/src/developer/bugreport/tests/BUILD.gn
@@ -13,11 +13,15 @@
{
name = "bugreport_integration_test"
},
+ {
+ name = "command_line_options_unittest"
+ },
]
deps = [
":bugreport_integration_test",
":bugreport_unittest",
+ ":command_line_options_unittest",
]
}
@@ -65,6 +69,20 @@
]
}
+executable("command_line_options_unittest") {
+ testonly = true
+
+ sources = [
+ "command_line_options_unittest.cc",
+ ]
+
+ deps = [
+ "//src/developer/bugreport:command_line_options",
+ "//src/lib/fxl/test:gtest_main",
+ "//third_party/googletest:gtest",
+ ]
+}
+
if (current_toolchain == host_toolchain) {
group("bugreport_client_tests") {
testonly = true
diff --git a/src/developer/bugreport/tests/bug_reporter_integration_test.cc b/src/developer/bugreport/tests/bug_reporter_integration_test.cc
index 4647d1d..66ba8e8 100644
--- a/src/developer/bugreport/tests/bug_reporter_integration_test.cc
+++ b/src/developer/bugreport/tests/bug_reporter_integration_test.cc
@@ -32,7 +32,8 @@
};
TEST_F(BugReporterIntegrationTest, SmokeTest) {
- ASSERT_TRUE(MakeBugReport(environment_services_, json_path_.data()));
+ ASSERT_TRUE(MakeBugReport(environment_services_,
+ /*attachment_allowlist=*/{}, json_path_.data()));
std::string output;
ASSERT_TRUE(files::ReadFileToString(json_path_, &output));
diff --git a/src/developer/bugreport/tests/bug_reporter_unittest.cc b/src/developer/bugreport/tests/bug_reporter_unittest.cc
index 45e7c52..0bbb808 100644
--- a/src/developer/bugreport/tests/bug_reporter_unittest.cc
+++ b/src/developer/bugreport/tests/bug_reporter_unittest.cc
@@ -63,7 +63,7 @@
files::ScopedTempDir tmp_dir_;
};
-TEST_F(BugReporterTest, SmokeTest) {
+TEST_F(BugReporterTest, Basic) {
ResetFeedbackDataProvider(/*annotations=*/
{
{"annotation.1.key", "annotation.1.value"},
@@ -76,7 +76,7 @@
});
ASSERT_TRUE(MakeBugReport(service_directory_provider_.service_directory(),
- json_path_.data()));
+ /*attachment_allowlist=*/{}, json_path_.data()));
std::string output;
ASSERT_TRUE(files::ReadFileToString(json_path_, &output));
@@ -117,6 +117,34 @@
"attachment.2.value");
}
+TEST_F(BugReporterTest, RestrictedAttachments) {
+ ResetFeedbackDataProvider(/*annotations=*/
+ {
+ {"annotation.key", "unused"},
+ },
+ /*attachments=*/{
+ {"attachment.key.filtered", "unused"},
+ {"attachment.key.kept", "unused"},
+ });
+
+ ASSERT_TRUE(MakeBugReport(service_directory_provider_.service_directory(),
+ /*attachment_allowlist=*/
+ {
+ "attachment.key.kept",
+ "attachment.key.ignored",
+ },
+ json_path_.data()));
+
+ std::string output;
+ ASSERT_TRUE(files::ReadFileToString(json_path_, &output));
+ rapidjson::Document document;
+ ASSERT_FALSE(document.Parse(output.c_str()).HasParseError());
+ ASSERT_TRUE(document.HasMember("attachments"));
+ ASSERT_FALSE(document["attachments"].HasMember("attachment.key.filtered"));
+ ASSERT_TRUE(document["attachments"].HasMember("attachment.key.kept"));
+ ASSERT_FALSE(document["attachments"].HasMember("attachment.key.ignored"));
+}
+
} // namespace
} // namespace bugreport
} // namespace fuchsia
diff --git a/src/developer/bugreport/tests/command_line_options_unittest.cc b/src/developer/bugreport/tests/command_line_options_unittest.cc
new file mode 100644
index 0000000..93f569b
--- /dev/null
+++ b/src/developer/bugreport/tests/command_line_options_unittest.cc
@@ -0,0 +1,75 @@
+// Copyright 2019 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 "src/developer/bugreport/command_line_options.h"
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+
+namespace fuchsia {
+namespace bugreport {
+namespace {
+
+TEST(CommandLineOptionsTest, Default) {
+ const char* argv[] = {"bugreport"};
+ EXPECT_EQ(ParseModeFromArgcArgv(1, argv), Mode::DEFAULT);
+}
+
+TEST(CommandLineOptionsTest, Minimal) {
+ const char* argv[] = {"bugreport", "--minimal"};
+ EXPECT_EQ(ParseModeFromArgcArgv(2, argv), Mode::MINIMAL);
+}
+
+TEST(CommandLineOptionsTest, Help) {
+ const char* argv[] = {"bugreport", "--help"};
+ EXPECT_EQ(ParseModeFromArgcArgv(2, argv), Mode::HELP);
+}
+
+TEST(CommandLineOptionsTest, HelpAnywhere) {
+ const char* argv[] = {"bugreport", "--irrelevant", "--help"};
+ EXPECT_EQ(ParseModeFromArgcArgv(3, argv), Mode::HELP);
+}
+
+TEST(CommandLineOptionsTest, HelpAsPositionalArgument) {
+ const char* argv[] = {"bugreport", "help"};
+ EXPECT_EQ(ParseModeFromArgcArgv(2, argv), Mode::HELP);
+}
+
+TEST(CommandLineOptionsTest, HelpAsPositionalArgumentAnywhere) {
+ const char* argv[] = {"bugreport", "--irrelevant", "help"};
+ EXPECT_EQ(ParseModeFromArgcArgv(3, argv), Mode::HELP);
+}
+
+TEST(CommandLineOptionsTest, FailureUnknownOption) {
+ const char* argv[] = {"bugreport", "--unknown"};
+ EXPECT_EQ(ParseModeFromArgcArgv(2, argv), Mode::FAILURE);
+}
+
+TEST(CommandLineOptionsTest, FailureUnknownPositionalArgument) {
+ const char* argv[] = {"bugreport", "unknown"};
+ EXPECT_EQ(ParseModeFromArgcArgv(2, argv), Mode::FAILURE);
+}
+
+} // namespace
+
+// Pretty-prints Mode in gTest matchers instead of the default byte string in
+// case of failed expectations.
+void PrintTo(const Mode& mode, std::ostream* os) {
+ switch (mode) {
+ case Mode::FAILURE:
+ *os << "FAILURE";
+ return;
+ case Mode::HELP:
+ *os << "HELP";
+ return;
+ case Mode::MINIMAL:
+ *os << "MINIMAL";
+ return;
+ case Mode::DEFAULT:
+ *os << "DEFAULT";
+ return;
+ }
+}
+
+} // namespace bugreport
+} // namespace fuchsia
diff --git a/src/developer/bugreport/tests/meta/command_line_options_unittest.cmx b/src/developer/bugreport/tests/meta/command_line_options_unittest.cmx
new file mode 100644
index 0000000..b879612
--- /dev/null
+++ b/src/developer/bugreport/tests/meta/command_line_options_unittest.cmx
@@ -0,0 +1,5 @@
+{
+ "program": {
+ "binary": "test/command_line_options_unittest"
+ }
+}