<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<p>Ok, I disabled the C API tests with:
<a class="moz-txt-link-freetext" href="https://reviews.llvm.org/rG9c63e5b415d1">https://reviews.llvm.org/rG9c63e5b415d1</a><br>
<br>
On 06/08/2021 09:09, Valentin Churavy wrote:<br>
</p>
<blockquote type="cite"
cite="mid:CAPZmkw7RdKP=eQy03rQfVg1B+N2goAU02Jnh=BDgxXYAZ64S4Q@mail.gmail.com">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<div dir="auto">
<div>Yes these were added while the orc tests were temporarily
disabled.</div>
<div dir="auto"><br>
</div>
<div dir="auto">I am on vacation this week, so no objection from
me to disable them again for now.</div>
<div dir="auto"><br>
</div>
<div dir="auto">Best,</div>
<div dir="auto">Valentin<br>
<br>
<div class="gmail_quote" dir="auto">
<div dir="ltr" class="gmail_attr">On Thu, Aug 5, 2021, 18:48
Azhar Mohammed <<a href="mailto:azhar@apple.com"
moz-do-not-send="true">azhar@apple.com</a>> wrote:<br>
</div>
<blockquote class="gmail_quote" style="margin:0 0 0
.8ex;border-left:1px #ccc solid;padding-left:1ex">
<div
style="word-wrap:break-word;line-break:after-white-space">Can
you please disable those tests again until we have a
fix? <br>
<div><br>
<blockquote type="cite">
<div>On Aug 5, 2021, at 3:00 AM, Stefan Gränitz <<a
href="mailto:stefan.graenitz@gmail.com"
target="_blank" rel="noreferrer"
moz-do-not-send="true">stefan.graenitz@gmail.com</a>>
wrote:</div>
<br>
<div>
<div> Hi Azhar<br>
<br>
Thanks for the ping. You are right, my change
for re-enabling the OrcV2Examples test did not
only affect my own test but also others.<br>
@Valentin and Lang (cc) here: Did you add these
tests while they were temporarily disabled via
the lit.local.cfg?<br>
<br>
Not sure what exactly is the issue here. It
might be due to sanitizers, but it might as well
just be the only bot that runs these tests and
the actual issue is something else. May I
forward this to you?<br>
<br>
Thanks<br>
Stefan<br>
<br>
<div>On 04/08/2021 18:36, Azhar Mohammed wrote:<br>
</div>
<blockquote type="cite"> Hi Stefan
<div><br>
</div>
<div>I see the OrcV2 examples tests are
failing on Darwin, when built with the
sanitizers enabled. Can you please take a
look? </div>
<div><br>
</div>
<div>Refer to <a
href="https://green.lab.llvm.org/green/job/clang-stage2-cmake-RgSan/7992/testReport/"
target="_blank" rel="noreferrer"
moz-do-not-send="true">https://green.lab.llvm.org/green/job/clang-stage2-cmake-RgSan/7992/testReport/</a>. </div>
<div><br>
</div>
<div>
<pre style="box-sizing:inherit;font-size:var(--font-size-monospace);white-space:pre-wrap;word-wrap:break-word;margin-top:0px;margin-bottom:0px;color:rgb(51,51,51)">Failed Tests (5):
LLVM :: Examples/OrcV2Examples/orcv2-cbindings-add-object-file.test
LLVM :: Examples/OrcV2Examples/orcv2-cbindings-basic-usage.test
LLVM :: Examples/OrcV2Examples/orcv2-cbindings-lazy.test
LLVM :: Examples/OrcV2Examples/orcv2-cbindings-reflect-process-symbols.test
LLVM :: Examples/OrcV2Examples/orcv2-cbindings-removable-code.test</pre>
<div><br>
</div>
<div>Thanks</div>
<div>Azhar</div>
<div><br>
<blockquote type="cite">
<div>On Jul 29, 2021, at 5:41 AM, Stefan
Gränitz via llvm-commits <<a
href="mailto:llvm-commits@lists.llvm.org"
target="_blank" rel="noreferrer"
moz-do-not-send="true">llvm-commits@lists.llvm.org</a>>
wrote:</div>
<br>
<div>
<div><br>
Author: Stefan Gränitz<br>
Date: 2021-07-29T14:40:42+02:00<br>
New Revision:
058935145d6b0c600c35e8b83fa150896c725f8d<br>
<br>
URL: <a
href="https://github.com/llvm/llvm-project/commit/058935145d6b0c600c35e8b83fa150896c725f8d"
target="_blank" rel="noreferrer"
moz-do-not-send="true">https://github.com/llvm/llvm-project/commit/058935145d6b0c600c35e8b83fa150896c725f8d</a><br>
DIFF: <a
href="https://github.com/llvm/llvm-project/commit/058935145d6b0c600c35e8b83fa150896c725f8d.diff"
target="_blank" rel="noreferrer"
moz-do-not-send="true">https://github.com/llvm/llvm-project/commit/058935145d6b0c600c35e8b83fa150896c725f8d.diff</a><br>
<br>
LOG: [Orc][examples] Adopt
ExecutorProcessControl API and
re-enable LLJITWithRemoteDebugging<br>
<br>
The API change originated from
D104694. The
LLJITWithRemoteDebugging example and
the test for it were disabled while
it was in the works.<br>
<br>
Added: <br>
<br>
<br>
Modified: <br>
llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/CMakeLists.txt<br>
llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/LLJITWithRemoteDebugging.cpp<br>
llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.cpp<br>
llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.h<br>
llvm/test/Examples/OrcV2Examples/lljit-with-remote-debugging.test<br>
llvm/test/Examples/lit.local.cfg<br>
<br>
Removed: <br>
<br>
<br>
<br>
################################################################################<br>
diff --git
a/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/CMakeLists.txt
b/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/CMakeLists.txt<br>
index 06dfe88a898d5..2f8db1976107d
100644<br>
---
a/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/CMakeLists.txt<br>
+++
b/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/CMakeLists.txt<br>
@@ -10,7 +10,7 @@
set(LLVM_LINK_COMPONENTS<br>
nativecodegen<br>
)<br>
<br>
-if (LLVM_INCLUDE_UTILS AND NOT
LLVM_INCLUDE_UTILS)<br>
+if (LLVM_INCLUDE_UTILS)<br>
add_llvm_example(LLJITWithRemoteDebugging<br>
LLJITWithRemoteDebugging.cpp<br>
RemoteJITUtils.cpp<br>
@@ -18,7 +18,4 @@ if
(LLVM_INCLUDE_UTILS AND NOT
LLVM_INCLUDE_UTILS)<br>
DEPENDS<br>
llvm-jitlink-executor<br>
)<br>
-else()<br>
- # Use a temporary no-op target
until D104694 lands.<br>
-
add_custom_target(LLJITWithRemoteDebugging)<br>
endif()<br>
<br>
diff --git
a/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/LLJITWithRemoteDebugging.cpp
b/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/LLJITWithRemoteDebugging.cpp<br>
index ca0953550e090..227ad09d7ae79
100644<br>
---
a/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/LLJITWithRemoteDebugging.cpp<br>
+++
b/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/LLJITWithRemoteDebugging.cpp<br>
@@ -133,12 +133,12 @@ static
cl::opt<bool><br>
<br>
ExitOnError ExitOnErr;<br>
<br>
-static
std::unique_ptr<JITLinkExecutor>
connectExecutor(const char *Argv0,<br>
-
ExecutionSession
&ES) {<br>
+static
std::unique_ptr<JITLinkExecutor>
connectExecutor(const char *Argv0) {<br>
// Connect to a running
out-of-process executor through a
TCP socket.<br>
if (!OOPExecutorConnect.empty()) {<br>
std::unique_ptr<TCPSocketJITLinkExecutor> Exec =<br>
-
ExitOnErr(JITLinkExecutor::ConnectTCPSocket(OOPExecutorConnect,
ES));<br>
+
ExitOnErr(JITLinkExecutor::ConnectTCPSocket(OOPExecutorConnect,<br>
+
std::ref(ExitOnErr)));<br>
<br>
outs() << "Connected to
executor at " <<
OOPExecutorConnect << "\n";<br>
if (WaitForDebugger) {<br>
@@ -157,7 +157,7 @@ static
std::unique_ptr<JITLinkExecutor>
connectExecutor(const char *Argv0,<br>
<br>
outs() << "Found
out-of-process executor: " <<
Exec->getPath() << "\n";<br>
<br>
- ExitOnErr(Exec->launch(ES));<br>
+
ExitOnErr(Exec->launch(std::ref(ExitOnErr)));<br>
if (WaitForDebugger) {<br>
outs() << "Launched
executor in subprocess: " <<
Exec->getPID() << "\n"<br>
<< "Attach a
debugger and press any key to
continue.\n";<br>
@@ -177,11 +177,8 @@ int main(int
argc, char *argv[]) {<br>
ExitOnErr.setBanner(std::string(argv[0]) + ": ");<br>
cl::ParseCommandLineOptions(argc,
argv, "LLJITWithRemoteDebugging");<br>
<br>
- auto ES =
std::make_unique<ExecutionSession>();<br>
-
ES->setErrorReporter([&](Error
Err) { ExitOnErr(std::move(Err));
});<br>
-<br>
// Launch/connect the
out-of-process executor.<br>
-
std::unique_ptr<JITLinkExecutor>
Executor = connectExecutor(argv[0],
*ES);<br>
+
std::unique_ptr<JITLinkExecutor>
Executor = connectExecutor(argv[0]);<br>
<br>
// Load the given IR files.<br>
std::vector<ThreadSafeModule> TSMs;<br>
@@ -215,6 +212,8 @@ int main(int
argc, char *argv[]) {<br>
<br>
// Create LLJIT and destroy it
before disconnecting the target
process.<br>
{<br>
+
std::unique_ptr<ExecutionSession>
ES = Executor->startSession();<br>
+<br>
outs() << "Initializing
LLJIT for remote executor\n";<br>
auto J =
ExitOnErr(LLJITBuilder()<br>
.setExecutionSession(std::move(ES))<br>
@@ -253,6 +252,5 @@ int main(int
argc, char *argv[]) {<br>
outs() << "Exit code: "
<< Result << "\n";<br>
}<br>
<br>
-
ExitOnErr(Executor->disconnect());<br>
return 0;<br>
}<br>
<br>
diff --git
a/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.cpp
b/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.cpp<br>
index 8e4a283556617..2616dd225d021
100644<br>
---
a/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.cpp<br>
+++
b/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.cpp<br>
@@ -47,9 +47,9 @@ class
RemoteExecutorProcessControl<br>
public:<br>
using
BaseT::initializeORCRPCEPCBase;<br>
<br>
-
RemoteExecutorProcessControl(ExecutionSession
&ES,<br>
-
std::unique_ptr<RPCChannel>
Channel,<br>
-
std::unique_ptr<RPCEndpoint>
Endpoint);<br>
+
RemoteExecutorProcessControl(std::unique_ptr<RPCChannel>
Channel,<br>
+
std::unique_ptr<RPCEndpoint>
Endpoint,<br>
+
BaseT::ErrorReporter
ReportError);<br>
<br>
void initializeMemoryManagement();<br>
Error disconnect() override;<br>
@@ -64,10 +64,10 @@ class
RemoteExecutorProcessControl<br>
};<br>
<br>
RemoteExecutorProcessControl::RemoteExecutorProcessControl(<br>
- ExecutionSession &ES,
std::unique_ptr<RPCChannel>
Channel,<br>
-
std::unique_ptr<RPCEndpoint>
Endpoint)<br>
- :
BaseT(ES.getSymbolStringPool(),
*Endpoint,<br>
- [&ES](Error Err) {
ES.reportError(std::move(Err)); }),<br>
+
std::unique_ptr<RPCChannel>
Channel,
std::unique_ptr<RPCEndpoint>
Endpoint,<br>
+ BaseT::ErrorReporter
ReportError)<br>
+ :
BaseT(std::make_shared<SymbolStringPool>(),
*Endpoint,<br>
+
std::move(ReportError)),<br>
Channel(std::move(Channel)),
Endpoint(std::move(Endpoint)) {<br>
<br>
ListenerThread =
std::thread([&]() {<br>
@@ -109,11 +109,17 @@
JITLinkExecutor::~JITLinkExecutor()
= default;<br>
<br>
Expected<std::unique_ptr<ObjectLayer>><br>
JITLinkExecutor::operator()(ExecutionSession &ES, const Triple
&TT) {<br>
+ assert(EPC &&
"RemoteExecutorProcessControl must
be initialized");<br>
return
std::make_unique<ObjectLinkingLayer>(ES,
EPC->getMemMgr());<br>
}<br>
<br>
+std::unique_ptr<ExecutionSession> JITLinkExecutor::startSession()
{<br>
+ assert(OwnedEPC &&
"RemoteExecutorProcessControl must
be initialized");<br>
+ return
std::make_unique<ExecutionSession>(std::move(OwnedEPC));<br>
+}<br>
+<br>
Error
JITLinkExecutor::addDebugSupport(ObjectLayer
&ObjLayer) {<br>
- auto Registrar =
createJITLoaderGDBRegistrar(*EPC);<br>
+ auto Registrar =
createJITLoaderGDBRegistrar(EPC->getExecutionSession());<br>
if (!Registrar)<br>
return Registrar.takeError();<br>
<br>
@@ -127,7 +133,8 @@ Error
JITLinkExecutor::addDebugSupport(ObjectLayer
&ObjLayer) {<br>
Expected<std::unique_ptr<DefinitionGenerator>><br>
JITLinkExecutor::loadDylib(StringRef
RemotePath) {<br>
if (auto Handle =
EPC->loadDylib(RemotePath.data()))<br>
- return
std::make_unique<EPCDynamicLibrarySearchGenerator>(*EPC,
*Handle);<br>
+ return
std::make_unique<EPCDynamicLibrarySearchGenerator>(<br>
+
EPC->getExecutionSession(),
*Handle);<br>
else<br>
return Handle.takeError();<br>
}<br>
@@ -174,7 +181,8 @@
JITLinkExecutor::CreateLocal(std::string
ExecutablePath) {<br>
<br>
TCPSocketJITLinkExecutor::TCPSocketJITLinkExecutor(<br>
std::unique_ptr<RemoteExecutorProcessControl> EPC) {<br>
- this->EPC = std::move(EPC);<br>
+ this->OwnedEPC =
std::move(EPC);<br>
+ this->EPC =
this->OwnedEPC.get();<br>
}<br>
<br>
#ifndef LLVM_ON_UNIX<br>
@@ -197,7 +205,8 @@
JITLinkExecutor::ConnectTCPSocket(StringRef
NetworkAddress,<br>
<br>
#else<br>
<br>
-Error
ChildProcessJITLinkExecutor::launch(ExecutionSession
&ES) {<br>
+Error
ChildProcessJITLinkExecutor::launch(<br>
+
unique_function<void(Error)>
ErrorReporter) {<br>
constexpr int ReadEnd = 0;<br>
constexpr int WriteEnd = 1;<br>
<br>
@@ -252,13 +261,14 @@ Error
ChildProcessJITLinkExecutor::launch(ExecutionSession
&ES) {<br>
auto Endpoint =
std::make_unique<RemoteExecutorProcessControl::RPCEndpoint>(<br>
*Channel, true);<br>
<br>
- EPC =
std::make_unique<RemoteExecutorProcessControl>(ES,
std::move(Channel),<br>
-
std::move(Endpoint));<br>
+ OwnedEPC =
std::make_unique<RemoteExecutorProcessControl>(<br>
+ std::move(Channel),
std::move(Endpoint),
std::move(ErrorReporter));<br>
<br>
- if (auto Err =
EPC->initializeORCRPCEPCBase())<br>
- return
joinErrors(std::move(Err),
EPC->disconnect());<br>
+ if (auto Err =
OwnedEPC->initializeORCRPCEPCBase())<br>
+ return
joinErrors(std::move(Err),
OwnedEPC->disconnect());<br>
<br>
-
EPC->initializeMemoryManagement();<br>
+
OwnedEPC->initializeMemoryManagement();<br>
+ EPC = OwnedEPC.get();<br>
<br>
shared::registerStringError<RPCChannel>();<br>
return Error::success();<br>
@@ -305,7 +315,7 @@ static
Expected<int>
connectTCPSocketImpl(std::string
Host,<br>
<br>
Expected<std::unique_ptr<TCPSocketJITLinkExecutor>><br>
JITLinkExecutor::ConnectTCPSocket(StringRef NetworkAddress,<br>
-
ExecutionSession
&ES) {<br>
+
unique_function<void(Error)>
ErrorReporter) {<br>
auto CreateErr =
[NetworkAddress](StringRef Details)
{<br>
return
make_error<StringError>(<br>
formatv("Failed to connect
TCP socket '{0}': {1}",
NetworkAddress,<br>
@@ -332,7 +342,7 @@
JITLinkExecutor::ConnectTCPSocket(StringRef
NetworkAddress,<br>
*Channel, true);<br>
<br>
auto EPC =
std::make_unique<RemoteExecutorProcessControl>(<br>
- ES, std::move(Channel),
std::move(Endpoint));<br>
+ std::move(Channel),
std::move(Endpoint),
std::move(ErrorReporter));<br>
<br>
if (auto Err =
EPC->initializeORCRPCEPCBase())<br>
return
joinErrors(std::move(Err),
EPC->disconnect());<br>
<br>
diff --git
a/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.h
b/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.h<br>
index baa376003a67b..5b15b1e9964f7
100644<br>
---
a/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.h<br>
+++
b/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.h<br>
@@ -56,12 +56,16 @@ class
JITLinkExecutor {<br>
/// through a TCP socket. A valid
NetworkAddress provides hostname and
port,<br>
/// e.g. localhost:20000.<br>
static
Expected<std::unique_ptr<TCPSocketJITLinkExecutor>><br>
- ConnectTCPSocket(StringRef
NetworkAddress, ExecutionSession
&ES);<br>
+ ConnectTCPSocket(StringRef
NetworkAddress,<br>
+
unique_function<void(Error)>
ErrorReporter);<br>
<br>
// Implement
ObjectLinkingLayerCreator<br>
Expected<std::unique_ptr<ObjectLayer>>
operator()(ExecutionSession &,<br>
const Triple &);<br>
<br>
+
std::unique_ptr<ExecutionSession>
startSession();<br>
+ Error disconnect();<br>
+<br>
Error addDebugSupport(ObjectLayer
&ObjLayer);<br>
<br>
Expected<std::unique_ptr<DefinitionGenerator>><br>
@@ -69,12 +73,12 @@ class
JITLinkExecutor {<br>
<br>
Expected<int>
runAsMain(JITEvaluatedSymbol
MainSym,<br>
ArrayRef<std::string> Args);<br>
- Error disconnect();<br>
<br>
virtual ~JITLinkExecutor();<br>
<br>
protected:<br>
-
std::unique_ptr<RemoteExecutorProcessControl>
EPC;<br>
+
std::unique_ptr<RemoteExecutorProcessControl>
OwnedEPC{nullptr};<br>
+ RemoteExecutorProcessControl
*EPC{nullptr};<br>
<br>
JITLinkExecutor();<br>
};<br>
@@ -82,7 +86,7 @@ class
JITLinkExecutor {<br>
/// JITLinkExecutor that runs in a
child process on the local machine.<br>
class ChildProcessJITLinkExecutor :
public JITLinkExecutor {<br>
public:<br>
- Error launch(ExecutionSession
&ES);<br>
+ Error
launch(unique_function<void(Error)>
ErrorReporter);<br>
<br>
pid_t getPID() const { return
ProcessID; }<br>
StringRef getPath() const { return
ExecutablePath; }<br>
<br>
diff --git
a/llvm/test/Examples/OrcV2Examples/lljit-with-remote-debugging.test
b/llvm/test/Examples/OrcV2Examples/lljit-with-remote-debugging.test<br>
index a1ea7f9db1fd0..a09d72a76bef9
100644<br>
---
a/llvm/test/Examples/OrcV2Examples/lljit-with-remote-debugging.test<br>
+++
b/llvm/test/Examples/OrcV2Examples/lljit-with-remote-debugging.test<br>
@@ -1,10 +1,10 @@<br>
# This test makes sure that the
example builds and executes as
expected.<br>
# Instructions for debugging can be
found in
LLJITWithRemoteDebugging.cpp<br>
<br>
-# RUN: LLJITWithRemoteDebugging
%p/Inputs/argc_sub1_elf.ll |
FileCheck --check-prefix=CHECK1 %s<br>
-# CHECK1: Parsing input IR code
from: {{.*}}/Inputs/argc_sub1_elf.ll<br>
-# CHECK1: Running: main()<br>
-# CHECK1: Exit code: 0<br>
+# RUN: LLJITWithRemoteDebugging
%p/Inputs/argc_sub1_elf.ll |
FileCheck --check-prefix=CHECK0 %s<br>
+# CHECK0: Parsing input IR code
from: {{.*}}/Inputs/argc_sub1_elf.ll<br>
+# CHECK0: Running: main()<br>
+# CHECK0: Exit code: 0<br>
<br>
# RUN: LLJITWithRemoteDebugging
%p/Inputs/argc_sub1_elf.ll --args
2nd 3rd 4th | FileCheck
--check-prefix=CHECK3 %s<br>
# CHECK3: Parsing input IR code
from: {{.*}}/Inputs/argc_sub1_elf.ll<br>
<br>
diff --git
a/llvm/test/Examples/lit.local.cfg
b/llvm/test/Examples/lit.local.cfg<br>
index f23a918956ba7..a9f3860333603
100644<br>
---
a/llvm/test/Examples/lit.local.cfg<br>
+++
b/llvm/test/Examples/lit.local.cfg<br>
@@ -1,7 +1,5 @@<br>
-#if not config.build_examples or
sys.platform in ['win32']:<br>
-<br>
-# Mark both lljit-with-* tests
unsupported until D104694 lands.<br>
-config.unsupported = True<br>
+if not config.build_examples or
sys.platform in ['win32']:<br>
+ config.unsupported = True<br>
<br>
# Test discovery should ignore
subdirectories that contain test
inputs.<br>
config.excludes = ['Inputs']<br>
<br>
<br>
<br>
_______________________________________________<br>
llvm-commits mailing list<br>
<a
href="mailto:llvm-commits@lists.llvm.org"
target="_blank" rel="noreferrer"
moz-do-not-send="true">llvm-commits@lists.llvm.org</a><br>
<a
href="https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits"
target="_blank" rel="noreferrer"
moz-do-not-send="true">https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits</a><br>
</div>
</div>
</blockquote>
</div>
<br>
</div>
</blockquote>
<pre cols="72">--
<a href="https://weliveindetail.github.io/blog/about/" target="_blank" rel="noreferrer" moz-do-not-send="true">https://weliveindetail.github.io/blog/about/</a></pre>
</div>
</div>
</blockquote>
</div>
<br>
</div>
</blockquote>
</div>
</div>
</div>
</blockquote>
<pre class="moz-signature" cols="72">--
<a class="moz-txt-link-freetext" href="https://weliveindetail.github.io/blog/about/">https://weliveindetail.github.io/blog/about/</a></pre>
</body>
</html>