lshal: --debug omits info on parent objects.

Instead, print a short message that contains the information
on the row that it should look up.

Test: lshal --debug
Test: lshal debug -E android.hardware.health@2.0::IHealth/default
Test: lshal_test
Fixes: 160745145
Change-Id: I7d5ab9fba097c0fb0b080ec52682084c65ba9f50
This commit is contained in:
Yifan Hong 2020-07-09 16:38:18 -07:00
parent 5cceea8d0b
commit 6884b87a37
7 changed files with 132 additions and 9 deletions

View file

@ -39,7 +39,7 @@ Status DebugCommand::parseArgs(const Arg &arg) {
// Optargs cannnot be used because the flag should not be considered set
// if it should really be contained in mOptions.
if (std::string(arg.argv[optind]) == "-E") {
mExcludesParentInstances = true;
mParentDebugInfoLevel = ParentDebugInfoLevel::NOTHING;
optind++;
}
@ -67,7 +67,7 @@ Status DebugCommand::main(const Arg &arg) {
return mLshal.emitDebugInfo(
pair.first, pair.second.empty() ? "default" : pair.second, mOptions,
mExcludesParentInstances,
mParentDebugInfoLevel,
mLshal.out().buf(),
mLshal.err());
}

View file

@ -21,6 +21,7 @@
#include <android-base/macros.h>
#include "Command.h"
#include "ParentDebugInfoLevel.h"
#include "utils.h"
namespace android {
@ -42,9 +43,8 @@ private:
std::string mInterfaceName;
std::vector<std::string> mOptions;
// Outputs the actual descriptor of a hal instead of the debug output
// if the arguments provided are a superclass of the actual hal impl.
bool mExcludesParentInstances;
// See comment on ParentDebugInfoLevel.
ParentDebugInfoLevel mParentDebugInfoLevel = ParentDebugInfoLevel::FULL;
DISALLOW_COPY_AND_ASSIGN(DebugCommand);
};

View file

@ -555,7 +555,7 @@ void ListCommand::dumpTable(const NullableOStream<std::ostream>& out) const {
std::stringstream ss;
auto pair = splitFirst(iName, '/');
mLshal.emitDebugInfo(pair.first, pair.second, {},
false /* excludesParentInstances */, ss,
ParentDebugInfoLevel::FQNAME_ONLY, ss,
NullableOStream<std::ostream>(nullptr));
return ss.str();
};

View file

@ -101,7 +101,7 @@ Status Lshal::emitDebugInfo(
const std::string &interfaceName,
const std::string &instanceName,
const std::vector<std::string> &options,
bool excludesParentInstances,
ParentDebugInfoLevel parentDebugInfoLevel,
std::ostream &out,
NullableOStream<std::ostream> err) const {
using android::hidl::base::V1_0::IBase;
@ -126,7 +126,7 @@ Status Lshal::emitDebugInfo(
return NO_INTERFACE;
}
if (excludesParentInstances) {
if (parentDebugInfoLevel != ParentDebugInfoLevel::FULL) {
const std::string descriptor = getDescriptor(base.get());
if (descriptor.empty()) {
std::string msg = interfaceName + "/" + instanceName + " getDescriptor failed";
@ -134,6 +134,9 @@ Status Lshal::emitDebugInfo(
LOG(ERROR) << msg;
}
if (descriptor != interfaceName) {
if (parentDebugInfoLevel == ParentDebugInfoLevel::FQNAME_ONLY) {
out << "[See " << descriptor << "/" << instanceName << "]";
}
return OK;
}
}

View file

@ -25,6 +25,7 @@
#include "Command.h"
#include "NullableOStream.h"
#include "ParentDebugInfoLevel.h"
#include "utils.h"
namespace android {
@ -49,7 +50,7 @@ public:
const std::string &interfaceName,
const std::string &instanceName,
const std::vector<std::string> &options,
bool excludesParentInstances,
ParentDebugInfoLevel parentDebugInfoLevel,
std::ostream &out,
NullableOStream<std::ostream> err) const;

View file

@ -0,0 +1,34 @@
/*
* Copyright (C) 2020 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.
*/
#pragma once
namespace android {
namespace lshal {
// Describe verbosity when dumping debug information on a HAL service by
// referring to a parent HAL interface FQName (for example, when dumping debug information
// on foo@1.0::IFoo but the HAL implementation is foo@1.1::IFoo).
enum class ParentDebugInfoLevel {
// Write nothing.
NOTHING,
// Write a short description that includes the FQName of the real implementation.
FQNAME_ONLY,
// Write full debug info.
FULL,
};
} // namespace lshal
} // namespace android

View file

@ -793,6 +793,91 @@ TEST_F(ListTest, Vintf) {
EXPECT_EQ("", err.str());
}
// Fake service returned by mocked IServiceManager::get for DumpDebug.
// The interfaceChain and getHashChain functions returns
// foo(id - 1) -> foo(id - 2) -> ... foo1 -> IBase.
class InheritingService : public IBase {
public:
explicit InheritingService(pid_t id) : mId(id) {}
android::hardware::Return<void> interfaceDescriptor(interfaceDescriptor_cb cb) override {
cb(getInterfaceName(mId));
return hardware::Void();
}
android::hardware::Return<void> interfaceChain(interfaceChain_cb cb) override {
std::vector<hidl_string> ret;
for (auto i = mId; i > 0; --i) {
ret.push_back(getInterfaceName(i));
}
ret.push_back(IBase::descriptor);
cb(ret);
return hardware::Void();
}
android::hardware::Return<void> getHashChain(getHashChain_cb cb) override {
std::vector<hidl_hash> ret;
for (auto i = mId; i > 0; --i) {
ret.push_back(getHashFromId(i));
}
ret.push_back(getHashFromId(0xff));
cb(ret);
return hardware::Void();
}
android::hardware::Return<void> debug(const hidl_handle& hh,
const hidl_vec<hidl_string>&) override {
const native_handle_t* handle = hh.getNativeHandle();
if (handle->numFds < 1) {
return Void();
}
int fd = handle->data[0];
std::string content = "debug info for ";
content += getInterfaceName(mId);
ssize_t written = write(fd, content.c_str(), content.size());
if (written != (ssize_t)content.size()) {
LOG(WARNING) << "SERVER(" << descriptor << ") debug writes " << written << " bytes < "
<< content.size() << " bytes, errno = " << errno;
}
return Void();
}
private:
pid_t mId;
};
TEST_F(ListTest, DumpDebug) {
size_t inheritanceLevel = 3;
sp<IBase> service = new InheritingService(inheritanceLevel);
EXPECT_CALL(*serviceManager, list(_)).WillRepeatedly(Invoke([&](IServiceManager::list_cb cb) {
std::vector<hidl_string> ret;
for (auto i = 1; i <= inheritanceLevel; ++i) {
ret.push_back(getInterfaceName(i) + "/default");
}
cb(ret);
return hardware::Void();
}));
EXPECT_CALL(*serviceManager, get(_, _))
.WillRepeatedly(
Invoke([&](const hidl_string&, const hidl_string& instance) -> sp<IBase> {
int id = getIdFromInstanceName(instance);
if (id > inheritanceLevel) return nullptr;
return sp<IBase>(service);
}));
const std::string expected = "[fake description 0]\n"
"Interface\n"
"a.h.foo1@1.0::IFoo/default\n"
"[See a.h.foo3@3.0::IFoo/default]\n"
"a.h.foo2@2.0::IFoo/default\n"
"[See a.h.foo3@3.0::IFoo/default]\n"
"a.h.foo3@3.0::IFoo/default\n"
"debug info for a.h.foo3@3.0::IFoo\n"
"\n";
optind = 1; // mimic Lshal::parseArg()
EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--types=b", "-id"})));
EXPECT_EQ(expected, out.str());
EXPECT_EQ("", err.str());
}
class ListVintfTest : public ListTest {
public:
virtual void SetUp() override {