Skip to content

Commit 289d53c

Browse files
committed
Update
[ghstack-poisoned]
2 parents 7bce689 + d208351 commit 289d53c

33 files changed

+1938
-98
lines changed

backends/arm/scripts/build_executorch_runner.sh

+20-5
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ toolchain_cmake=${et_root_dir}/examples/arm/ethos-u-setup/arm-none-eabi-gcc.cmak
1414
pte_file=""
1515
target="ethos-u55-128"
1616
build_type="Release"
17-
system_config=""
1817
bundleio=false
18+
system_config=""
19+
memory_mode=""
1920
build_with_etdump=false
2021
extra_build_flags=""
2122
output_folder_set=false
@@ -32,9 +33,12 @@ help() {
3233
echo " --pte=<PTE_FILE> pte file (genrated by the aot_arm_compier from the model to include in the elf"
3334
echo " --target=<TARGET> Target to build and run for Default: ${target}"
3435
echo " --build_type=<TYPE> Build with Release, Debug or RelWithDebInfo, default is ${build_type}"
35-
echo " --system_config=<CONFIG> System configuration to select from the Vela configuration file (see vela.ini). Default: Ethos_U55_High_End_Embedded for EthosU55 targets, Ethos_U85_SYS_DRAM_Mid for EthosU85 targets."
36-
echo " NOTE: If given, this option must match the given target. This option also sets timing adapter values customized for specific hardware, see ./executor_runner/CMakeLists.txt."
3736
echo " --bundleio Support both pte and Bundle IO bpte using Devtools BundelIO with Input/RefOutput included"
37+
echo " --system_config=<CONFIG> System configuration to select from the Vela configuration file (see vela.ini). Default: Ethos_U55_High_End_Embedded for EthosU55 targets, Ethos_U85_SYS_DRAM_Mid for EthosU85 targets."
38+
echo " NOTE: If given, this option must match the given target. This option along with the memory_mode sets timing adapter values customized for specific hardware, see ./executor_runner/CMakeLists.txt."
39+
echo " --memory_mode=<CONFIG> Vela memory mode, used for setting the Timing Adapter parameters of the Corstone platforms."
40+
echo " Valid values are Shared_Sram(for Ethos-U55, Ethos-U65, Ethos-85), Sram_Only(for Ethos-U55, Ethos-U65, Ethos-U85) or Dedicated_Sram(for Ethos-U65, Ethos-U85)."
41+
echo " Default: Shared_Sram for the Ethos-U55 and Sram_Only for the Ethos-U85"
3842
echo " --etdump Adds Devtools etdump support to track timing, etdump area will be base64 encoded in the log"
3943
echo " --extra_build_flags=<FLAGS> Extra flags to pass to cmake like -DET_ARM_BAREMETAL_METHOD_ALLOCATOR_POOL_SIZE=60000 Default: none "
4044
echo " --output=<FOLDER> Output folder Default: <MODEL>/<MODEL>_<TARGET INFO>.pte"
@@ -49,8 +53,9 @@ for arg in "$@"; do
4953
--pte=*) pte_file="${arg#*=}";;
5054
--target=*) target="${arg#*=}";;
5155
--build_type=*) build_type="${arg#*=}";;
52-
--system_config=*) system_config="${arg#*=}";;
5356
--bundleio) bundleio=true ;;
57+
--system_config=*) system_config="${arg#*=}";;
58+
--memory_mode=*) memory_mode="${arg#*=}";;
5459
--etdump) build_with_etdump=true ;;
5560
--extra_build_flags=*) extra_build_flags="${arg#*=}";;
5661
--output=*) output_folder="${arg#*=}" ; output_folder_set=true ;;
@@ -83,6 +88,15 @@ then
8388
fi
8489
fi
8590

91+
if [[ ${memory_mode} == "" ]]
92+
then
93+
memory_mode="Shared_Sram"
94+
if [[ ${target} =~ "ethos-u85" ]]
95+
then
96+
memory_mode="Sram_Only"
97+
fi
98+
fi
99+
86100
output_folder=$(realpath ${output_folder})
87101

88102
if [[ ${target} == *"ethos-u55"* ]]; then
@@ -91,7 +105,7 @@ else
91105
target_cpu=cortex-m85
92106
fi
93107
echo "--------------------------------------------------------------------------------"
94-
echo "Build Arm Baremetal executor_runner for ${target} with ${pte_file} using ${system_config} ${extra_build_flags} to '${output_folder}/cmake-out'"
108+
echo "Build Arm Baremetal executor_runner for ${target} with ${pte_file} using ${system_config} ${memory_mode} ${extra_build_flags} to '${output_folder}/cmake-out'"
95109
echo "--------------------------------------------------------------------------------"
96110

97111
cd ${et_root_dir}/examples/arm/executor_runner
@@ -120,6 +134,7 @@ cmake \
120134
${build_with_etdump_flags} \
121135
-DPYTHON_EXECUTABLE=$(which python3) \
122136
-DSYSTEM_CONFIG=${system_config} \
137+
-DMEMORY_MODE=${memory_mode} \
123138
${extra_build_flags} \
124139
-B ${output_folder}/cmake-out
125140

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
#include <executorch/devtools/etdump/data_sinks/file_data_sink.h>
10+
#include <cstdio> // For FILE operations
11+
12+
using ::executorch::runtime::Error;
13+
using ::executorch::runtime::Result;
14+
15+
namespace executorch {
16+
namespace etdump {
17+
18+
FileDataSink::FileDataSink(FileDataSink&& other) noexcept
19+
: file_(other.file_), total_written_bytes_(other.total_written_bytes_) {
20+
other.file_ = nullptr;
21+
}
22+
23+
Result<FileDataSink> FileDataSink::create(const char* file_path) {
24+
// Open the file and get the file pointer
25+
FILE* file = fopen(file_path, "wb");
26+
if (!file) {
27+
// Return an error if the file cannot be accessed or created
28+
ET_LOG(Error, "File %s cannot be accessed or created.", file_path);
29+
return Error::AccessFailed;
30+
}
31+
32+
// Return the successfully created FileDataSink
33+
return FileDataSink(file);
34+
}
35+
36+
FileDataSink::~FileDataSink() {
37+
// Close the file
38+
close();
39+
}
40+
41+
Result<size_t> FileDataSink::write(const void* ptr, size_t size) {
42+
if (!file_) {
43+
ET_LOG(Error, "File not open, unable to write.");
44+
return Error::AccessFailed;
45+
}
46+
47+
size_t offset = total_written_bytes_;
48+
49+
if (size == 0) {
50+
// No data to write, return current offset
51+
return offset;
52+
}
53+
54+
size_t written = fwrite(ptr, 1, size, file_);
55+
if (written != size) {
56+
ET_LOG(Error, "Write failed: wrote %zu bytes of %zu", written, size);
57+
return Error::Internal;
58+
}
59+
60+
total_written_bytes_ += written;
61+
return offset;
62+
}
63+
64+
size_t FileDataSink::get_used_bytes() const {
65+
return total_written_bytes_;
66+
}
67+
68+
void FileDataSink::close() {
69+
if (file_) {
70+
fclose(file_);
71+
file_ = nullptr;
72+
}
73+
}
74+
75+
} // namespace etdump
76+
} // namespace executorch
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
#pragma once
10+
11+
#include <executorch/devtools/etdump/data_sinks/data_sink_base.h>
12+
#include <executorch/runtime/core/exec_aten/exec_aten.h>
13+
#include <cstdio> // For FILE operations
14+
15+
namespace executorch {
16+
namespace etdump {
17+
18+
/**
19+
* FileDataSink is a concrete implementation of the DataSinkBase class,
20+
* designed to facilitate the direct writing of data to a file. It is
21+
* particularly useful for scenarios where immediate data storage is
22+
* required, such as logging or streaming data to a file for real-time
23+
* analysis. The class manages file operations, including opening, writing,
24+
* and closing the file, while handling potential errors during these
25+
* operations.
26+
*/
27+
28+
class FileDataSink : public DataSinkBase {
29+
public:
30+
/**
31+
* Creates a FileDataSink with a given file path.
32+
*
33+
* @param[in] file_path The path to the file for writing data.
34+
* @return A Result object containing either:
35+
* - A FileDataSink object if succees, or
36+
* - AccessFailed Error when the file cannot be accessed or created
37+
*/
38+
static ::executorch::runtime::Result<FileDataSink> create(
39+
const char* file_path);
40+
41+
/**
42+
* Destructor that closes the file.
43+
*/
44+
~FileDataSink() override;
45+
46+
// Delete copy constructor and copy assignment operator
47+
FileDataSink(const FileDataSink&) = delete;
48+
FileDataSink& operator=(const FileDataSink&) = delete;
49+
50+
FileDataSink(FileDataSink&& other) noexcept;
51+
FileDataSink& operator=(FileDataSink&& other) = default;
52+
53+
/**
54+
* Writes data directly to the file.
55+
*
56+
* This function does not perform any alignment, and will overwrite
57+
* any existing data in the file.
58+
*
59+
* @param[in] ptr A pointer to the data to be written into the file.
60+
* @param[in] size The size of the data in bytes.
61+
* @return A Result object containing either:
62+
* - The offset of the starting location of the data within the
63+
* file, or
64+
* - AccessFailedError if the file has been closed.
65+
* - InternalError if the os write operation fails.
66+
*/
67+
::executorch::runtime::Result<size_t> write(const void* ptr, size_t size)
68+
override;
69+
70+
/**
71+
* Gets the number of bytes currently written to the file.
72+
*
73+
* @return The amount of data currently stored in bytes.
74+
*/
75+
size_t get_used_bytes() const override;
76+
77+
/**
78+
* Closes the file, if it is open.
79+
*/
80+
void close();
81+
82+
private:
83+
/**
84+
* Constructs a FileDataSink with a given file pointer.
85+
*
86+
* @param[in] file A valid file pointer for writing data.
87+
*/
88+
explicit FileDataSink(FILE* file) : file_(file), total_written_bytes_(0) {}
89+
90+
FILE* file_;
91+
size_t total_written_bytes_;
92+
};
93+
94+
} // namespace etdump
95+
} // namespace executorch

devtools/etdump/data_sinks/targets.bzl

+1
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,4 @@ def define_common_targets():
4747
)
4848

4949
define_data_sink_target("buffer_data_sink", aten_suffix)
50+
define_data_sink_target("file_data_sink", aten_suffix)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
#include <executorch/devtools/etdump/data_sinks/file_data_sink.h>
10+
#include <executorch/runtime/platform/runtime.h>
11+
#include <gtest/gtest.h>
12+
#include <stdio.h> // tmpnam(), remove()
13+
#include <fstream>
14+
15+
using namespace ::testing;
16+
using ::executorch::etdump::FileDataSink;
17+
using ::executorch::runtime::Error;
18+
using ::executorch::runtime::Result;
19+
20+
class FileDataSinkTest : public ::testing::Test {
21+
protected:
22+
void SetUp() override {
23+
// Initialize the runtime environment
24+
torch::executor::runtime_init();
25+
26+
// Define the file path for testing
27+
std::array<char, L_tmpnam> buf;
28+
const char* ret = std::tmpnam(buf.data());
29+
ASSERT_NE(ret, nullptr) << "Could not generate temp file";
30+
buf[L_tmpnam - 1] = '\0';
31+
file_path_ = std::string(buf.data()) + "-executorch-testing";
32+
}
33+
34+
void TearDown() override {
35+
// Remove the test file
36+
std::remove(file_path_.c_str());
37+
}
38+
39+
std::string file_path_;
40+
};
41+
42+
TEST_F(FileDataSinkTest, CreationExpectFail) {
43+
// Create a FileDataSink instance with a valid file path
44+
Result<FileDataSink> success = FileDataSink::create(file_path_.c_str());
45+
ASSERT_TRUE(success.ok());
46+
47+
// Try to create another FileDataSink instance with an invalid file path
48+
Result<FileDataSink> fail_with_invalid_file_path = FileDataSink::create("");
49+
ASSERT_EQ(fail_with_invalid_file_path.error(), Error::AccessFailed);
50+
}
51+
52+
TEST_F(FileDataSinkTest, WriteDataToFile) {
53+
const char* data = "Hello, World!";
54+
size_t data_size = strlen(data);
55+
56+
// Create a FileDataSink instance
57+
Result<FileDataSink> result = FileDataSink::create(file_path_.c_str());
58+
ASSERT_TRUE(result.ok());
59+
60+
FileDataSink* data_sink = &result.get();
61+
62+
// Write data to the file
63+
Result<size_t> write_result = data_sink->write(data, data_size);
64+
ASSERT_TRUE(write_result.ok());
65+
66+
size_t used_bytes = data_sink->get_used_bytes();
67+
EXPECT_EQ(used_bytes, data_size);
68+
69+
data_sink->close();
70+
71+
// Expect fail if write again after close
72+
Result<size_t> write_result_after_close = data_sink->write(data, data_size);
73+
ASSERT_EQ(write_result_after_close.error(), Error::AccessFailed);
74+
75+
// Verify the file contents
76+
std::ifstream file(file_path_, std::ios::binary);
77+
file.seekg(0, std::ios::end);
78+
size_t file_size = file.tellg();
79+
file.seekg(0, std::ios::beg);
80+
EXPECT_EQ(file_size, used_bytes);
81+
82+
// Read the file content and verify it matches the original data
83+
std::vector<char> file_content(file_size);
84+
file.read(file_content.data(), file_size);
85+
file.close();
86+
87+
EXPECT_EQ(std::memcmp(file_content.data(), data, data_size), 0);
88+
}
89+
90+
TEST_F(FileDataSinkTest, WriteMultipleDataAndCheckOffsets) {
91+
const char* data1 = "Accelerate";
92+
const char* data2 = "Core";
93+
const char* data3 = "Experience";
94+
size_t data1_size = strlen(data1);
95+
size_t data2_size = strlen(data2);
96+
size_t data3_size = strlen(data3);
97+
98+
// Create a FileDataSink instance
99+
Result<FileDataSink> result = FileDataSink::create(file_path_.c_str());
100+
ASSERT_TRUE(result.ok());
101+
FileDataSink* data_sink = &result.get();
102+
103+
// Write multiple data chunks and check offsets
104+
Result<size_t> offset1 = data_sink->write(data1, data1_size);
105+
ASSERT_TRUE(offset1.ok());
106+
EXPECT_EQ(offset1.get(), 0);
107+
108+
Result<size_t> offset2 = data_sink->write(data2, data2_size);
109+
ASSERT_TRUE(offset2.ok());
110+
EXPECT_EQ(offset2.get(), data1_size);
111+
112+
Result<size_t> offset3 = data_sink->write(data3, data3_size);
113+
ASSERT_TRUE(offset3.ok());
114+
EXPECT_EQ(offset3.get(), data1_size + data2_size);
115+
size_t used_bytes = data_sink->get_used_bytes();
116+
EXPECT_EQ(used_bytes, data1_size + data2_size + data3_size);
117+
118+
data_sink->close();
119+
120+
// Verify the file contents
121+
std::ifstream file(file_path_, std::ios::binary);
122+
file.seekg(0, std::ios::end);
123+
size_t file_size = file.tellg();
124+
file.seekg(0, std::ios::beg);
125+
EXPECT_EQ(file_size, used_bytes);
126+
127+
// Read the file content
128+
std::vector<char> file_content(file_size);
129+
file.read(file_content.data(), file_size);
130+
file.close();
131+
132+
// Verify each data chunk in the file using offsets
133+
EXPECT_EQ(
134+
std::memcmp(file_content.data() + offset1.get(), data1, data1_size), 0);
135+
EXPECT_EQ(
136+
std::memcmp(file_content.data() + offset2.get(), data2, data2_size), 0);
137+
EXPECT_EQ(
138+
std::memcmp(file_content.data() + offset3.get(), data3, data3_size), 0);
139+
}

0 commit comments

Comments
 (0)