Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Hexagon] Add support for on-device unit testing using gtest #11145

Merged
merged 20 commits into from
May 3, 2022
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,15 @@ endif()
target_link_libraries(tvm PRIVATE ${TVM_LINKER_LIBS} ${TVM_RUNTIME_LINKER_LIBS})
target_link_libraries(tvm_runtime PRIVATE ${TVM_RUNTIME_LINKER_LIBS})

if(BUILD_FOR_HEXAGON AND DEFINED USE_HEXAGON_GTEST AND EXISTS ${USE_HEXAGON_GTEST})
adstraw marked this conversation as resolved.
Show resolved Hide resolved
include(FetchContent)
FetchContent_Declare(googletest SOURCE_DIR "${USE_HEXAGON_GTEST}")
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
target_link_libraries(tvm_runtime PUBLIC gtest)
adstraw marked this conversation as resolved.
Show resolved Hide resolved
include_directories("${USE_HEXAGON_GTEST}/include")
endif()

# Set flags for clang
include(cmake/modules/ClangFlags.cmake)
set(CRC16_INCLUDE_PATH "3rdparty/libcrc/include")
Expand Down Expand Up @@ -634,7 +643,6 @@ if(GTEST_FOUND)
tvm_file_glob(GLOB_RECURSE TEST_SRCS tests/cpp/*.cc)
add_executable(cpptest ${TEST_SRCS})
# include runtime files for unit testing
target_include_directories(cpptest PUBLIC "src/runtime")
target_link_libraries(cpptest PRIVATE ${TVM_TEST_LIBRARY_NAME} GTest::GTest GTest::Main GTest::gmock pthread dl)
set_target_properties(cpptest PROPERTIES EXCLUDE_FROM_ALL 1)
set_target_properties(cpptest PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD 1)
Expand All @@ -649,6 +657,9 @@ add_custom_target(runtime DEPENDS tvm_runtime)
# Installation rules
install(TARGETS tvm EXPORT ${PROJECT_NAME}Targets DESTINATION lib${LIB_SUFFIX})
install(TARGETS tvm_runtime EXPORT ${PROJECT_NAME}Targets DESTINATION lib${LIB_SUFFIX})
if(BUILD_FOR_HEXAGON AND DEFINED USE_HEXAGON_GTEST AND EXISTS ${USE_HEXAGON_GTEST})
install(TARGETS gtest EXPORT ${PROJECT_NAME}Targets DESTINATION lib${LIB_SUFFIX})
endif()

if (INSTALL_DEV)
install(
Expand Down
9 changes: 9 additions & 0 deletions apps/hexagon_api/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ include(ExternalProject)
# USE_HEXAGON_TOOLCHAIN (Path to Hexagon toolchain ending with "Tools")
# Optional variable:
# USE_OUTPUT_BINARY_DIR (Path to copy the output binaries to)
# USE_HEXAGON_GTEST (Path to Hexagon gtest)

set(TVM_SOURCE_DIR "${CMAKE_SOURCE_DIR}/../..")

Expand All @@ -23,6 +24,11 @@ else()
endif()
file(MAKE_DIRECTORY ${HEXAGON_API_BINARY_DIR})

if(DEFINED USE_HEXAGON_GTEST AND NOT EXISTS $USE_HEXAGON_GTEST)
message(WARNING "The specified Hexagon gtest path USE_HEXAGON_GTEST = ${USE_HEXAGON_GTEST} does not exist. Disabling Hexagon gtest support.")
unset(USE_HEXAGON_GTEST)
endif()

# Build X86 binaries:
# - tvm_rpc_x86

Expand Down Expand Up @@ -109,6 +115,9 @@ ExternalProject_Add(hexagon_tvm_runtime_rpc
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
"-DUSE_ALTERNATIVE_LINKER=OFF"
"-DUSE_CUSTOM_LOGGING=ON"
if(DEFINED USE_HEXAGON_GTEST)
"-DUSE_HEXAGON_GTEST=${USE_HEXAGON_GTEST}"
endif()
INSTALL_COMMAND ""
BUILD_ALWAYS ON
)
Expand Down
24 changes: 15 additions & 9 deletions cmake/modules/Hexagon.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,6 @@ if(NOT USE_HEXAGON)
if(BUILD_FOR_HOST)
list(APPEND COMPILER_SRCS src/target/opt/build_hexagon_off.cc)
endif()
list(APPEND RUNTIME_SRCS src/runtime/hexagon/hexagon_buffer.cc)
list(APPEND RUNTIME_SRCS src/runtime/hexagon/hexagon_common.cc)
list(APPEND RUNTIME_SRCS src/runtime/hexagon/hexagon_user_dma.cc)
return()
endif()

Expand Down Expand Up @@ -119,14 +116,23 @@ function(add_hexagon_wrapper_paths)
link_directories("${HEXAGON_TOOLCHAIN}/lib/iss")
endfunction()


# Common sources for TVM runtime with Hexagon support
file_glob_append(RUNTIME_HEXAGON_SRCS
"${TVMRT_SOURCE_DIR}/hexagon/*.cc"
)

if(BUILD_FOR_HEXAGON OR USE_HEXAGON_RPC)
# Common sources for TVM runtime with Hexagon support
file_glob_append(RUNTIME_HEXAGON_SRCS
"${TVMRT_SOURCE_DIR}/hexagon/*.cc"
)
else()
file_glob_append(RUNTIME_HEXAGON_SRCS
"${TVMRT_SOURCE_DIR}/hexagon/hexagon_module.cc"
)
endif()
Comment on lines +119 to +128
Copy link
Contributor

@kparzysz-quic kparzysz-quic May 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part disables Hexagon runtime when building for x86. Please revert this.

Nevermind. It was an error on my side. Sorry for the noise.


if(BUILD_FOR_HEXAGON)
if(DEFINED USE_HEXAGON_GTEST AND EXISTS ${USE_HEXAGON_GTEST})
file_glob_append(RUNTIME_HEXAGON_SRCS
"${TVMRT_SOURCE_DIR}/hexagon/tests/*.cc"
)
endif()
get_hexagon_sdk_property("${USE_HEXAGON_SDK}" "${USE_HEXAGON_ARCH}"
SDK_INCLUDE SDK_INCLUDE_DIRS
QURT_INCLUDE QURT_INCLUDE_DIRS
Expand Down
25 changes: 5 additions & 20 deletions src/runtime/hexagon/hexagon_buffer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -52,26 +52,15 @@ struct Allocation {

struct DDRAllocation : public Allocation {
DDRAllocation(size_t nbytes, size_t alignment) : Allocation(nbytes, alignment) {
#ifdef _WIN32
data_ = _aligned_malloc(nbytes, alignment);
CHECK(data_ != nullptr);
#else
int ret = posix_memalign(&data_, alignment, nbytes);
CHECK_EQ(ret, 0);
#endif
}
~DDRAllocation() {
#ifdef _WIN32
_aligned_free(data_);
#else
free(data_);
#endif
}
~DDRAllocation() { free(data_); }
};

#if defined(__hexagon__)
struct VTCMAllocation : public Allocation {
VTCMAllocation(size_t nbytes, size_t alignment) : Allocation(nbytes, alignment) {
#if defined(__hexagon__)
compute_res_attr_t res_info;
HEXAGON_SAFE_CALL(HAP_compute_res_attr_init(&res_info));

Expand All @@ -94,20 +83,16 @@ struct VTCMAllocation : public Allocation {
LOG(ERROR) << "ERROR: Unable to acquire requeisted resource.";
return;
}
// LOG(INFO) << "VTCMAllocation() - Context ID: " << context_id_ << ", VTCM ptr: " << data_;
#endif
}
~VTCMAllocation() {
// LOG(INFO) << "~VTCMAllocation() - Context ID: " << context_id_ << ", VTCM ptr: " << data_;
#if defined(__hexagon__)
HEXAGON_SAFE_CALL(HAP_compute_res_release(context_id_));
data_ = nullptr;
#endif
}
unsigned int context_id_{0};
};
#else
struct VTCMAllocation : public DDRAllocation {
VTCMAllocation(size_t nbytes, size_t alignment) : DDRAllocation(nbytes, alignment) {}
};
#endif

template <HexagonBuffer::StorageScope S>
std::unique_ptr<Allocation> Allocator(size_t nbytes, size_t alignment);
Expand Down
13 changes: 1 addition & 12 deletions src/runtime/hexagon/hexagon_user_dma.cc
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,10 @@ int hexagon_user_dma_1d_sync_helper(void* dst, void* src, uint32_t length) {

void* dma_desc = nullptr;

#ifdef _WIN32
dma_desc = _aligned_malloc(DMA_DESC_2D_SIZE, DMA_DESC_2D_SIZE);
#else
int ret = posix_memalign(&dma_desc, DMA_DESC_2D_SIZE, DMA_DESC_2D_SIZE);
if (ret) {
return DMA_FAILURE;
}
#endif

if (!dma_desc) {
return DMA_FAILURE;
Expand All @@ -98,20 +94,13 @@ int hexagon_user_dma_1d_sync_helper(void* dst, void* src, uint32_t length) {
unsigned int status = dmwait() & DM0_STATUS_MASK;
unsigned int done = dma_desc_get_done(dma_desc);

#ifdef _WIN32
_aligned_free(dma_desc);
#else
free(dma_desc);
#endif

if (status == DM0_STATUS_IDLE && done == DESC_DONE_COMPLETE) {
return DMA_SUCCESS;
}
return DMA_FAILURE;
#else
memcpy(dst, src, length);
return DMA_SUCCESS;
#endif
return DMA_FAILURE;
}

int hexagon_user_dma_1d_sync(void* dst, void* src, uint32_t length) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@
*/

#include <gtest/gtest.h>
#include <hexagon/hexagon_buffer.h>
#include <tvm/runtime/container/optional.h>

#include "../hexagon_buffer.h"

using namespace tvm::runtime;
using namespace tvm::runtime::hexagon;

Expand Down
60 changes: 60 additions & 0 deletions src/runtime/hexagon/tests/run_all_tests.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 <gtest/gtest.h>
#include <tvm/runtime/packed_func.h>
#include <tvm/runtime/registry.h>

#include <string>
#include <vector>

#include "../src/support/utils.h"

namespace tvm {
namespace runtime {
namespace hexagon {

TVM_REGISTER_GLOBAL("hexagon.run_all_tests").set_body([](TVMArgs args, TVMRetValue* rv) {
// gtest args are passed into this packed func as a singular string
// split gtest args using <space> delimiter and build argument vector
std::vector<std::string> parsed_args = tvm::support::Split(args[0], ' ');
std::vector<char*> argv;

// add executable name
argv.push_back(const_cast<char*>("hexagon_run_all_tests"));

// add parsed arguments
for (int i = 0; i < parsed_args.size(); ++i) {
argv.push_back(const_cast<char*>(parsed_args[i].data()));
}

// end of parsed arguments
argv.push_back(nullptr);

// set argument count
int argc = argv.size() - 1;

// initialize gtest with arguments and run
::testing::InitGoogleTest(&argc, argv.data());
*rv = RUN_ALL_TESTS();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neat! looks like gtest supports capturing the stdout to a string that we can return in the ret value across the FFI,

testing::internal::CaptureStdout();
// Run tests
// ...
std::string output = testing::internal::GetCapturedStdout();

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, it looks like stdout redirection is one of the disabled features in the Hexagon SDK gtest version.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How so? in that case can we target a generic gtest and then modify to support for Hexagon as we have discussed potentially doing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is that one must set GTEST_HAS_STREAM_REDIRECTION=0 when compiling gtest for Hexagon which disables the CaptureStdout function. I believe this would be true even if we used a "stock" gtest version as opposed to the gtest version in the Hexagon SDK.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Darn. @kparzysz-quic any idea why this is disabled for Hexagon? Is there an alternative for us to capture stdout on Hexagon?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know. What happens if we set GTEST_HAS_STREAM_REDIRECTION=1?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GTest stream redirection relies on dup2(), which does not exist under QuRT.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

});

} // namespace hexagon
} // namespace runtime
} // namespace tvm
42 changes: 42 additions & 0 deletions tests/python/contrib/test_hexagon/unit_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.

import pytest
import numpy as np
from tvm.contrib.hexagon.build import HexagonLauncher
from .conftest import requires_hexagon_toolchain


@requires_hexagon_toolchain
def test_cache_read_write_2d(hexagon_session):
# arguments to pass to gtest
# e.g.
# 1) to run all tests use:
# gtest_args = ""
# 2) to run all tests with "foo" in their name twice use:
# gtest_args = "--gtest_repeat=2 --gtest_filter=*foo*"
gtest_args = ""
try:
func = hexagon_session._rpc.get_function("hexagon.run_all_tests")
result = func(gtest_args)
except:
print(
"This test requires the USE_HEXAGON_GTEST cmake flag to be specified with a path to a Hexagon gtest version normally located at /path/to/hexagon/sdk/utils/googletest/gtest"
)
result = 1

np.testing.assert_equal(result, 0)
3 changes: 2 additions & 1 deletion tests/scripts/task_build_hexagon_api.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ cmake -DANDROID_ABI=arm64-v8a \
-DUSE_HEXAGON_ARCH=v68 \
-DUSE_HEXAGON_SDK="${HEXAGON_SDK_PATH}" \
-DUSE_HEXAGON_TOOLCHAIN="${HEXAGON_TOOLCHAIN}" \
-DUSE_OUTPUT_BINARY_DIR="${output_binary_directory}" ..
-DUSE_OUTPUT_BINARY_DIR="${output_binary_directory}" \
-DUSE_HEXAGON_GTEST="${HEXAGON_SDK_PATH}/utils/googletest/gtest" ..

make -j$(nproc)