Skip to content

Commit

Permalink
1
Browse files Browse the repository at this point in the history
  • Loading branch information
xinyiZzz committed May 9, 2024
1 parent f452e95 commit 9ead678
Show file tree
Hide file tree
Showing 20 changed files with 198 additions and 27 deletions.
5 changes: 5 additions & 0 deletions be/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ option(USE_LIBCPP "Use libc++" OFF)
option(USE_MEM_TRACKER, "Use memory tracker" ON)
option(USE_UNWIND "Use libunwind" ON)
option(USE_JEMALLOC "Use jemalloc" ON)
option(USE_JEMALLOC_HOOK "Use jemalloc hook" ON)
if (OS_MACOSX)
set(GLIBC_COMPATIBILITY OFF)
set(USE_LIBCPP ON)
Expand All @@ -87,6 +88,7 @@ message(STATUS "GLIBC_COMPATIBILITY is ${GLIBC_COMPATIBILITY}")
message(STATUS "USE_LIBCPP is ${USE_LIBCPP}")
message(STATUS "USE_MEM_TRACKER is ${USE_MEM_TRACKER}")
message(STATUS "USE_JEMALLOC is ${USE_JEMALLOC}")
message(STATUS "USE_JEMALLOC_HOOK is ${USE_JEMALLOC_HOOK}")
message(STATUS "USE_UNWIND is ${USE_UNWIND}")
message(STATUS "ENABLE_PCH is ${ENABLE_PCH}")

Expand Down Expand Up @@ -345,6 +347,9 @@ endif()
if (USE_JEMALLOC)
add_definitions(-DUSE_JEMALLOC)
endif()
if (USE_JEMALLOC_HOOK)
add_definitions(-DUSE_JEMALLOC_HOOK)
endif()

# Compile with libunwind
if (USE_UNWIND)
Expand Down
3 changes: 3 additions & 0 deletions be/src/common/daemon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@
// IWYU pragma: no_include <bthread/errno.h>
#include <errno.h> // IWYU pragma: keep
#include <gflags/gflags.h>
#if !defined(__SANITIZE_ADDRESS__) && !defined(ADDRESS_SANITIZER) && !defined(LEAK_SANITIZER) && \
!defined(THREAD_SANITIZER) && !defined(USE_JEMALLOC)
#include <gperftools/malloc_extension.h> // IWYU pragma: keep
#endif
// IWYU pragma: no_include <bits/std_abs.h>
#include <butil/iobuf.h>
#include <math.h>
Expand Down
4 changes: 4 additions & 0 deletions be/src/http/action/jeprofile_actions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,11 @@ void JeHeapAction::handle(HttpRequest* req) {
<< "." << rand() << ".heap";
const std::string& tmp_file_name_str = tmp_jeprof_file_name.str();
const char* file_name_ptr = tmp_file_name_str.c_str();
#ifdef USE_JEMALLOC_HOOK
int result = jemallctl("prof.dump", nullptr, nullptr, &file_name_ptr, sizeof(const char*));
#else
int result = mallctl("prof.dump", nullptr, nullptr, &file_name_ptr, sizeof(const char*));
#endif
std::stringstream response;
if (result == 0) {
response << "Jemalloc heap dump success, dump file path: " << tmp_jeprof_file_name.str()
Expand Down
3 changes: 3 additions & 0 deletions be/src/http/action/pprof_actions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@

#include "http/action/pprof_actions.h"

#if !defined(__SANITIZE_ADDRESS__) && !defined(ADDRESS_SANITIZER) && !defined(LEAK_SANITIZER) && \
!defined(THREAD_SANITIZER) && !defined(USE_JEMALLOC)
#include <gperftools/heap-profiler.h> // IWYU pragma: keep
#include <gperftools/malloc_extension.h> // IWYU pragma: keep
#include <gperftools/profiler.h> // IWYU pragma: keep
#endif
#include <stdio.h>

#include <fstream>
Expand Down
8 changes: 7 additions & 1 deletion be/src/http/default_path_handlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
#include <boost/algorithm/string/replace.hpp>
#ifdef USE_JEMALLOC
#include "jemalloc/jemalloc.h"
#else
#endif
#if !defined(__SANITIZE_ADDRESS__) && !defined(ADDRESS_SANITIZER) && !defined(LEAK_SANITIZER) && \
!defined(THREAD_SANITIZER) && !defined(USE_JEMALLOC)
#include <gperftools/malloc_extension.h>
#endif

Expand Down Expand Up @@ -112,7 +114,11 @@ void mem_usage_handler(const WebPageHandler::ArgumentMap& args, std::stringstrea
auto* _opaque = static_cast<std::string*>(opaque);
_opaque->append(buf);
};
#ifdef USE_JEMALLOC_HOOK
jemalloc_stats_print(write_cb, &tmp, "a");
#else
malloc_stats_print(write_cb, &tmp, "a");
#endif
boost::replace_all(tmp, "\n", "<br>");
(*output) << tmp << "</pre>";
#else
Expand Down
3 changes: 3 additions & 0 deletions be/src/pch/pch.h
Original file line number Diff line number Diff line change
Expand Up @@ -231,11 +231,14 @@
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/port.h>

#if !defined(__SANITIZE_ADDRESS__) && !defined(ADDRESS_SANITIZER) && !defined(LEAK_SANITIZER) && \
!defined(THREAD_SANITIZER) && !defined(USE_JEMALLOC)
// gperftools headers
#include <gperftools/malloc_extension.h>
#include <gperftools/malloc_hook.h>
#include <gperftools/nallocx.h>
#include <gperftools/tcmalloc.h>
#endif

// hs headers
#include <hs/hs.h>
Expand Down
2 changes: 1 addition & 1 deletion be/src/runtime/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ set(EXECUTABLE_OUTPUT_PATH "${BUILD_DIR}/src/runtime")

file(GLOB_RECURSE RUNTIME_FILES CONFIGURE_DEPENDS *.cpp *.cc)

if (NOT USE_JEMALLOC OR NOT USE_MEM_TRACKER)
if (NOT USE_JEMALLOC OR NOT USE_JEMALLOC_HOOK)
list(REMOVE_ITEM RUNTIME_FILES ${CMAKE_CURRENT_SOURCE_DIR}/memory/jemalloc_hook.cpp)
endif()

Expand Down
20 changes: 20 additions & 0 deletions be/src/runtime/fragment_mgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,26 @@ Status FragmentMgr::exec_plan_fragment(const TPipelineFragmentParams& params,
std::shared_ptr<QueryContext> query_ctx;
RETURN_IF_ERROR(_get_query_ctx(params, params.query_id, true, query_ctx));
SCOPED_ATTACH_TASK_WITH_ID(query_ctx->query_mem_tracker, params.query_id);
LOG(INFO) << "222222 ";
#ifdef USE_JEMALLOC_HOOK
int64_t scope_mem = 0;
void* ptr1;
{
SCOPED_MEM_COUNT_BY_HOOK(&scope_mem);
ptr1 = malloc(1000);
}
free(ptr1);
LOG(INFO) << "111111 " << std::to_string(scope_mem);
#else
int64_t scope_mem = 0;
void* ptr1;
{
SCOPED_MEM_COUNT_BY_HOOK(&scope_mem);
ptr1 = malloc(1000);
}
free(ptr1);
LOG(INFO) << "111111 " << std::to_string(scope_mem);
#endif
DCHECK((params.query_options.__isset.enable_pipeline_x_engine &&
params.query_options.enable_pipeline_x_engine) ||
(params.query_options.__isset.enable_pipeline_engine &&
Expand Down
3 changes: 3 additions & 0 deletions be/src/runtime/memory/tcmalloc_hook.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

#pragma once

#if !defined(__SANITIZE_ADDRESS__) && !defined(ADDRESS_SANITIZER) && !defined(LEAK_SANITIZER) && \
!defined(THREAD_SANITIZER) && !defined(USE_JEMALLOC)
#include <gperftools/malloc_hook.h>
#include <gperftools/nallocx.h>
#include <gperftools/tcmalloc.h>
Expand Down Expand Up @@ -53,3 +55,4 @@ void init_hook() {
// MallocHook::RemoveNewHook(&new_hook);
// MallocHook::RemoveDeleteHook(&delete_hook);
// }
#endif
31 changes: 17 additions & 14 deletions be/src/runtime/thread_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,18 +59,6 @@
#define SCOPED_CONSUME_MEM_TRACKER(mem_tracker) \
auto VARNAME_LINENUM(add_mem_consumer) = doris::AddThreadMemTrackerConsumer(mem_tracker)

// Count a code segment memory (memory malloc - memory free) to int64_t
// Usage example: int64_t scope_mem = 0; { SCOPED_MEM_COUNT(&scope_mem); xxx; xxx; }
#define SCOPED_MEM_COUNT_BY_HOOK(scope_mem) \
auto VARNAME_LINENUM(scope_mem_count) = doris::ScopeMemCountByHook(scope_mem)

// Count a code segment memory (memory malloc - memory free) to MemTracker.
// Compared to count `scope_mem`, MemTracker is easier to observe from the outside and is thread-safe.
// Usage example: std::unique_ptr<MemTracker> tracker = std::make_unique<MemTracker>("first_tracker");
// { SCOPED_CONSUME_MEM_TRACKER_BY_HOOK(_mem_tracker.get()); xxx; xxx; }
#define SCOPED_CONSUME_MEM_TRACKER_BY_HOOK(mem_tracker) \
auto VARNAME_LINENUM(add_mem_consumer) = doris::AddThreadMemTrackerConsumerByHook(mem_tracker)

#define ORPHAN_TRACKER_CHECK() \
DCHECK(doris::k_doris_exit || !doris::config::enable_memory_orphan_check || \
doris::thread_context()->thread_mem_tracker()->label() != "Orphan") \
Expand All @@ -89,12 +77,27 @@
auto VARNAME_LINENUM(scoped_tls_stmtl) = doris::ScopedInitThreadContext()
#define SCOPED_CONSUME_MEM_TRACKER(mem_tracker) \
auto VARNAME_LINENUM(scoped_tls_cmt) = doris::ScopedInitThreadContext()
#define ORPHAN_TRACKER_CHECK() (void)0
#define MEMORY_ORPHAN_CHECK() (void)0
#endif

#if defined(USE_MEM_TRACKER) && !defined(BE_TEST) && defined(USE_JEMALLOC_HOOK)
// Count a code segment memory (memory malloc - memory free) to int64_t
// Usage example: int64_t scope_mem = 0; { SCOPED_MEM_COUNT_BY_HOOK(&scope_mem); xxx; xxx; }
#define SCOPED_MEM_COUNT_BY_HOOK(scope_mem) \
auto VARNAME_LINENUM(scope_mem_count) = doris::ScopeMemCountByHook(scope_mem)

// Count a code segment memory (memory malloc - memory free) to MemTracker.
// Compared to count `scope_mem`, MemTracker is easier to observe from the outside and is thread-safe.
// Usage example: std::unique_ptr<MemTracker> tracker = std::make_unique<MemTracker>("first_tracker");
// { SCOPED_CONSUME_MEM_TRACKER_BY_HOOK(_mem_tracker.get()); xxx; xxx; }
#define SCOPED_CONSUME_MEM_TRACKER_BY_HOOK(mem_tracker) \
auto VARNAME_LINENUM(add_mem_consumer) = doris::AddThreadMemTrackerConsumerByHook(mem_tracker)
#else
#define SCOPED_MEM_COUNT_BY_HOOK(scope_mem) \
auto VARNAME_LINENUM(scoped_tls_mcbh) = doris::ScopedInitThreadContext()
#define SCOPED_CONSUME_MEM_TRACKER_BY_HOOK(mem_tracker) \
auto VARNAME_LINENUM(scoped_tls_cmtbh) = doris::ScopedInitThreadContext()
#define ORPHAN_TRACKER_CHECK() (void)0
#define MEMORY_ORPHAN_CHECK() (void)0
#endif

#define SCOPED_SKIP_MEMORY_CHECK() \
Expand Down
3 changes: 3 additions & 0 deletions be/src/service/doris_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@
// IWYU pragma: no_include <bthread/errno.h>
#include <errno.h> // IWYU pragma: keep
#include <fcntl.h>
#if !defined(__SANITIZE_ADDRESS__) && !defined(ADDRESS_SANITIZER) && !defined(LEAK_SANITIZER) && \
!defined(THREAD_SANITIZER) && !defined(USE_JEMALLOC)
#include <gperftools/malloc_extension.h> // IWYU pragma: keep
#endif
#include <libgen.h>
#include <setjmp.h>
#include <signal.h>
Expand Down
4 changes: 4 additions & 0 deletions be/src/util/mem_info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,11 @@ void MemInfo::refresh_allocator_mem() {
// the current epoch number, which might be useful to log as a sanity check.
uint64_t epoch = 0;
size_t sz = sizeof(epoch);
#ifdef USE_JEMALLOC_HOOK
jemallctl("epoch", &epoch, &sz, &epoch, sz);
#else
mallctl("epoch", &epoch, &sz, &epoch, sz);
#endif

// https://jemalloc.net/jemalloc.3.html
// https://www.bookstack.cn/read/aliyun-rds-core/4a0cdf677f62feb3.md
Expand Down
15 changes: 14 additions & 1 deletion be/src/util/mem_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@
#include "common/logging.h"
#ifdef USE_JEMALLOC
#include "jemalloc/jemalloc.h"
#else
#endif
#if !defined(__SANITIZE_ADDRESS__) && !defined(ADDRESS_SANITIZER) && !defined(LEAK_SANITIZER) && \
!defined(THREAD_SANITIZER) && !defined(USE_JEMALLOC)
#include <gperftools/malloc_extension.h>
#endif
#include "common/config.h"
Expand Down Expand Up @@ -105,9 +107,15 @@ class MemInfo {
#ifdef USE_JEMALLOC
size_t value = 0;
size_t sz = sizeof(value);
#ifdef USE_JEMALLOC_HOOK
if (jemallctl(name.c_str(), &value, &sz, nullptr, 0) == 0) {
return value;
}
#else
if (mallctl(name.c_str(), &value, &sz, nullptr, 0) == 0) {
return value;
}
#endif
#endif
return 0;
}
Expand All @@ -128,8 +136,13 @@ class MemInfo {
if (config::enable_je_purge_dirty_pages) {
try {
// Purge all unused dirty pages for arena <i>, or for all arenas if <i> equals MALLCTL_ARENAS_ALL.
#ifdef USE_JEMALLOC_HOOK
jemallctl(fmt::format("arena.{}.purge", MALLCTL_ARENAS_ALL).c_str(), nullptr,
nullptr, nullptr, 0);
#else
mallctl(fmt::format("arena.{}.purge", MALLCTL_ARENAS_ALL).c_str(), nullptr, nullptr,
nullptr, 0);
#endif
} catch (...) {
LOG(WARNING) << "Purge all unused dirty pages for all arenas failed";
}
Expand Down
2 changes: 2 additions & 0 deletions bin/start_be.sh
Original file line number Diff line number Diff line change
Expand Up @@ -346,9 +346,11 @@ fi

if [[ -z ${JEMALLOC_PROF_PRFIX} ]]; then
export JEMALLOC_CONF="${JEMALLOC_CONF},prof_prefix:"
export MALLOC_CONF="${JEMALLOC_CONF},prof_prefix:"
else
JEMALLOC_PROF_PRFIX="${DORIS_HOME}/log/${JEMALLOC_PROF_PRFIX}"
export JEMALLOC_CONF="${JEMALLOC_CONF},prof_prefix:${JEMALLOC_PROF_PRFIX}"
export MALLOC_CONF="${JEMALLOC_CONF},prof_prefix:${JEMALLOC_PROF_PRFIX}"
fi

if [[ "${RUN_DAEMON}" -eq 1 ]]; then
Expand Down
29 changes: 29 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ set -eo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"

export DORIS_HOME="${ROOT}"
export TP_DIR="${ROOT}/thirdparty"
export TP_INSTALL_DIR="${TP_DIR:-.}/installed"
export TP_INCLUDE_DIR="${TP_INSTALL_DIR}/include"
export TP_LIB_DIR="${TP_INSTALL_DIR}/lib"

. "${DORIS_HOME}/env.sh"

Expand Down Expand Up @@ -357,6 +361,28 @@ fi
if [[ -z "${USE_JEMALLOC}" ]]; then
USE_JEMALLOC='ON'
fi
if [[ -z "${USE_JEMALLOC_HOOK}" ]]; then
USE_JEMALLOC_HOOK='OFF'
fi
# update jemalloc prefix
rm -rf "${TP_INCLUDE_DIR}/jemalloc/jemalloc.h"
rm -rf "${TP_LIB_DIR}/libjemalloc_doris.a"
rm -rf "${TP_LIB_DIR}/libjemalloc_doris_pic.a"
rm -rf "${TP_INCLUDE_DIR}/rocksdb"
rm -rf "${TP_LIB_DIR}/librocksdb.a"
if [[ "${USE_JEMALLOC_HOOK}" == "ON" ]]; then
cp "${TP_INCLUDE_DIR}/jemalloc/jemalloc_doris_with_prefix.h" "${TP_INCLUDE_DIR}/jemalloc/jemalloc.h"
cp "${TP_LIB_DIR}/libjemalloc_doris_with_prefix.a" "${TP_LIB_DIR}/libjemalloc_doris.a"
cp "${TP_LIB_DIR}/libjemalloc_doris_with_prefix_pic.a" "${TP_LIB_DIR}/libjemalloc_doris_pic.a"
cp "${TP_LIB_DIR}/librocksdb_jemalloc_with_prefix.a" "${TP_LIB_DIR}/librocksdb.a"
cp -r "${TP_INCLUDE_DIR}/rocksdb_jemalloc_with_prefix" "${TP_INCLUDE_DIR}/rocksdb"
else
cp "${TP_INCLUDE_DIR}/jemalloc/jemalloc_doris_no_prefix.h" "${TP_INCLUDE_DIR}/jemalloc/jemalloc.h"
cp "${TP_LIB_DIR}/libjemalloc_doris_no_prefix.a" "${TP_LIB_DIR}/libjemalloc_doris.a"
cp "${TP_LIB_DIR}/libjemalloc_doris_no_prefix_pic.a" "${TP_LIB_DIR}/libjemalloc_doris_pic.a"
cp "${TP_LIB_DIR}/librocksdb_jemalloc_no_prefix.a" "${TP_LIB_DIR}/librocksdb.a"
cp -r "${TP_INCLUDE_DIR}/rocksdb_jemalloc_no_prefix" "${TP_INCLUDE_DIR}/rocksdb"
fi
if [[ -z "${USE_BTHREAD_SCANNER}" ]]; then
USE_BTHREAD_SCANNER='OFF'
fi
Expand Down Expand Up @@ -462,6 +488,7 @@ echo "Get params:
STRIP_DEBUG_INFO -- ${STRIP_DEBUG_INFO}
USE_MEM_TRACKER -- ${USE_MEM_TRACKER}
USE_JEMALLOC -- ${USE_JEMALLOC}
USE_JEMALLOC_HOOK -- ${USE_JEMALLOC_HOOK}
USE_BTHREAD_SCANNER -- ${USE_BTHREAD_SCANNER}
ENABLE_STACKTRACE -- ${ENABLE_STACKTRACE}
ENABLE_INJECTION_POINT -- ${ENABLE_INJECTION_POINT}
Expand Down Expand Up @@ -564,6 +591,7 @@ if [[ "${BUILD_BE}" -eq 1 ]]; then
-DENABLE_PCH="${ENABLE_PCH}" \
-DUSE_MEM_TRACKER="${USE_MEM_TRACKER}" \
-DUSE_JEMALLOC="${USE_JEMALLOC}" \
-DUSE_JEMALLOC_HOOK="${USE_JEMALLOC_HOOK}" \
-DENABLE_STACKTRACE="${ENABLE_STACKTRACE}" \
-DUSE_AVX2="${USE_AVX2}" \
-DGLIBC_COMPATIBILITY="${GLIBC_COMPATIBILITY}" \
Expand Down Expand Up @@ -607,6 +635,7 @@ if [[ "${BUILD_CLOUD}" -eq 1 ]]; then
-DSTRIP_DEBUG_INFO="${STRIP_DEBUG_INFO}" \
-DUSE_DWARF="${USE_DWARF}" \
-DUSE_JEMALLOC="${USE_JEMALLOC}" \
-DUSE_JEMALLOC_HOOK="${USE_JEMALLOC_HOOK}" \
-DEXTRA_CXX_FLAGS="${EXTRA_CXX_FLAGS}" \
-DBUILD_CHECK_META="${BUILD_CHECK_META:-OFF}" \
"${DORIS_HOME}/cloud/"
Expand Down
3 changes: 3 additions & 0 deletions cloud/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,9 @@ endif ()
if (USE_JEMALLOC)
set(CXX_COMMON_FLAGS "${CXX_COMMON_FLAGS} -DUSE_JEMALLOC")
endif()
if (USE_JEMALLOC_HOOK)
set(CXX_COMMON_FLAGS "${CXX_COMMON_FLAGS} -DUSE_JEMALLOC_HOOK")
endif()

if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0)
set(CXX_COMMON_FLAGS "${CXX_COMMON_FLAGS} -faligned-new")
Expand Down
1 change: 1 addition & 0 deletions cloud/cmake/thirdparty.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ add_thirdparty(thrift)
add_thirdparty(crypto)
add_thirdparty(openssl LIBNAME "lib/libssl.a")
add_thirdparty(jemalloc LIBNAME "lib/libjemalloc_doris.a")
add_thirdparty(jemalloc_arrow LIBNAME "lib/libjemalloc_arrow.a")
add_thirdparty(leveldb) # Required by brpc
add_thirdparty(brpc LIB64)
add_thirdparty(rocksdb) # For local storage mocking
Expand Down
2 changes: 1 addition & 1 deletion cloud/src/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ set(COMMON_FILES
network_util.cpp
)

if (USE_JEMALLOC)
if (USE_JEMALLOC AND USE_JEMALLOC_HOOK)
set(COMMON_FILES ${COMMON_FILES}
jemalloc_hook.cpp
)
Expand Down
5 changes: 3 additions & 2 deletions regression-test/pipeline/performance/compile.sh
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,14 @@ sudo docker run -i --rm \
-v "${teamcity_build_checkoutDir}":/root/doris \
"${docker_image}" \
/bin/bash -c "mkdir -p ${git_storage_path} \
&& cp -r /root/git/* ${git_storage_path}/ \
&& cp -r /root/git/* ${git_storage_path}/ \
&& cd /root/doris \
&& export CCACHE_LOGFILE=/tmp/cache.debug \
&& export CCACHE_REMOTE_STORAGE=file:///root/ccache \
&& export EXTRA_CXX_FLAGS=-O3 \
&& export USE_JEMALLOC='ON' \
&& export ENABLE_PCH=OFF ${jdk17_str}\
&& export USE_JEMALLOC_HOOK_WITH_PREFIX='OFF' \
&& export ENABLE_PCH=OFF ${jdk17_str}\
&& export CUSTOM_NPM_REGISTRY=https://registry.npmjs.org \
&& bash build.sh --fe --be --clean 2>&1 | tee build.log"
set +x
Expand Down
Loading

0 comments on commit 9ead678

Please sign in to comment.