Merge "init: clarify multiple 'interface' keyword use"
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 4723606..56a0679 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -194,26 +194,26 @@
return true;
}
-void Elf::GetInfo(Memory* memory, bool* valid, uint64_t* size) {
+bool Elf::GetInfo(Memory* memory, uint64_t* size) {
if (!IsValidElf(memory)) {
- *valid = false;
- return;
+ return false;
}
*size = 0;
- *valid = true;
- // Now read the section header information.
uint8_t class_type;
if (!memory->ReadFully(EI_CLASS, &class_type, 1)) {
- return;
+ return false;
}
+
+ // Get the maximum size of the elf data from the header.
if (class_type == ELFCLASS32) {
ElfInterface32::GetMaxSize(memory, size);
} else if (class_type == ELFCLASS64) {
ElfInterface64::GetMaxSize(memory, size);
} else {
- *valid = false;
+ return false;
}
+ return true;
}
bool Elf::IsValidPc(uint64_t pc) {
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 64005ae..52b7535 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -53,10 +53,8 @@
return nullptr;
}
- bool valid;
uint64_t max_size;
- Elf::GetInfo(memory.get(), &valid, &max_size);
- if (!valid) {
+ if (!Elf::GetInfo(memory.get(), &max_size)) {
// Init as if the whole file is an elf.
if (memory->Init(name, 0)) {
elf_offset = offset;
@@ -109,11 +107,7 @@
// first part of the elf file. This is done if the linker rosegment
// option is used.
std::unique_ptr<MemoryRange> memory(new MemoryRange(process_memory, start, end - start, 0));
- bool valid;
- uint64_t max_size;
- Elf::GetInfo(memory.get(), &valid, &max_size);
- if (valid) {
- // Valid elf, we are done.
+ if (Elf::IsValidElf(memory.get())) {
return memory.release();
}
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 099cc9e..ee1cd1a 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -190,6 +190,12 @@
FillInDexFrame();
// Clear the dex pc so that we don't repeat this frame later.
regs_->set_dex_pc(0);
+
+ // Make sure there is enough room for the real frame.
+ if (frames_.size() == max_frames_) {
+ last_error_.code = ERROR_MAX_FRAMES_EXCEEDED;
+ break;
+ }
}
FillInFrame(map_info, elf, rel_pc, step_pc, pc_adjustment);
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index f4cdbda..9af859d 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -94,7 +94,7 @@
static bool IsValidElf(Memory* memory);
- static void GetInfo(Memory* memory, bool* valid, uint64_t* size);
+ static bool GetInfo(Memory* memory, uint64_t* size);
static uint64_t GetLoadBias(Memory* memory);
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 4369030..831d3b5 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -946,6 +946,33 @@
EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
}
+TEST_F(UnwinderTest, dex_pc_max_frames) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x10000);
+ regs_.FakeSetDexPc(0xa3400);
+
+ Unwinder unwinder(1, &maps_, ®s_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0x400U, frame->rel_pc);
+ EXPECT_EQ(0xa3400U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/fake/fake.vdex", frame->map_name);
+ EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0xa3000U, frame->map_start);
+ EXPECT_EQ(0xa4000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+}
+
// Verify format frame code.
TEST_F(UnwinderTest, format_frame_static) {
FrameData frame;
diff --git a/llkd/include/llkd.h b/llkd/include/llkd.h
index 1e2df2f..4d39dd9 100644
--- a/llkd/include/llkd.h
+++ b/llkd/include/llkd.h
@@ -57,7 +57,7 @@
#define LLK_BLACKLIST_UID_PROPERTY "ro.llk.blacklist.uid"
#define LLK_BLACKLIST_UID_DEFAULT ""
#define LLK_BLACKLIST_STACK_PROPERTY "ro.llk.blacklist.process.stack"
-#define LLK_BLACKLIST_STACK_DEFAULT "init,lmkd.llkd,llkd,keystore,/system/bin/keystore,ueventd"
+#define LLK_BLACKLIST_STACK_DEFAULT "init,lmkd.llkd,llkd,keystore,/system/bin/keystore,ueventd,apexd"
/* clang-format on */
__END_DECLS
diff --git a/lmkd/include/lmkd.h b/lmkd/include/lmkd.h
index fe6364d..e8f51da 100644
--- a/lmkd/include/lmkd.h
+++ b/lmkd/include/lmkd.h
@@ -30,6 +30,7 @@
LMK_TARGET = 0, /* Associate minfree with oom_adj_score */
LMK_PROCPRIO, /* Register a process and set its oom_adj_score */
LMK_PROCREMOVE, /* Unregister a process */
+ LMK_PROCPURGE, /* Purge all registered processes */
};
/*
@@ -142,6 +143,15 @@
return 2 * sizeof(int);
}
+/*
+ * Prepare LMK_PROCPURGE packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline size_t lmkd_pack_set_procpurge(LMKD_CTRL_PACKET packet) {
+ packet[0] = htonl(LMK_PROCPURGE);
+ return sizeof(int);
+}
+
__END_DECLS
#endif /* _LMKD_H_ */
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 8b68dca..3a3d5db 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -612,9 +612,38 @@
}
lmkd_pack_get_procremove(packet, ¶ms);
+ /*
+ * WARNING: After pid_remove() procp is freed and can't be used!
+ * Therefore placed at the end of the function.
+ */
pid_remove(params.pid);
}
+static void cmd_procpurge() {
+ int i;
+ struct proc *procp;
+ struct proc *next;
+
+ if (use_inkernel_interface) {
+ return;
+ }
+
+ for (i = 0; i <= ADJTOSLOT(OOM_SCORE_ADJ_MAX); i++) {
+ procadjslot_list[i].next = &procadjslot_list[i];
+ procadjslot_list[i].prev = &procadjslot_list[i];
+ }
+
+ for (i = 0; i < PIDHASH_SZ; i++) {
+ procp = pidhash[i];
+ while (procp) {
+ next = procp->pidhash_next;
+ free(procp);
+ procp = next;
+ }
+ }
+ memset(&pidhash[0], 0, sizeof(pidhash));
+}
+
static void cmd_target(int ntargets, LMKD_CTRL_PACKET packet) {
int i;
struct lmk_target target;
@@ -757,6 +786,11 @@
goto wronglen;
cmd_procremove(packet);
break;
+ case LMK_PROCPURGE:
+ if (nargs != 0)
+ goto wronglen;
+ cmd_procpurge();
+ break;
default:
ALOGE("Received unknown command code %d", cmd);
return;
@@ -1135,6 +1169,7 @@
char *taskname;
int tasksize;
int r;
+ int result = -1;
#ifdef LMKD_LOG_STATS
struct memory_stat mem_st = {};
@@ -1143,14 +1178,12 @@
taskname = proc_get_name(pid);
if (!taskname) {
- pid_remove(pid);
- return -1;
+ goto out;
}
tasksize = proc_get_size(pid);
if (tasksize <= 0) {
- pid_remove(pid);
- return -1;
+ goto out;
}
#ifdef LMKD_LOG_STATS
@@ -1169,13 +1202,12 @@
r = kill(pid, SIGKILL);
ALOGI("Kill '%s' (%d), uid %d, oom_adj %d to free %ldkB",
taskname, pid, uid, procp->oomadj, tasksize * page_k);
- pid_remove(pid);
TRACE_KILL_END();
if (r) {
ALOGE("kill(%d): errno=%d", pid, errno);
- return -1;
+ goto out;
} else {
#ifdef LMKD_LOG_STATS
if (memory_stat_parse_result == 0) {
@@ -1187,10 +1219,16 @@
-1, -1, tasksize * BYTES_IN_KILOBYTE, -1, -1);
}
#endif
- return tasksize;
+ result = tasksize;
}
- return tasksize;
+out:
+ /*
+ * WARNING: After pid_remove() procp is freed and can't be used!
+ * Therefore placed at the end of the function.
+ */
+ pid_remove(pid);
+ return result;
}
/*
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
index 658e079..bd17555 100644
--- a/logd/FlushCommand.cpp
+++ b/logd/FlushCommand.cpp
@@ -42,7 +42,7 @@
LogTimeEntry::wrlock();
LastLogTimes::iterator it = times.begin();
while (it != times.end()) {
- entry = (*it);
+ entry = it->get();
if (entry->mClient == client) {
if (!entry->isWatchingMultiple(mLogMask)) {
LogTimeEntry::unlock();
@@ -63,31 +63,12 @@
}
}
entry->triggerReader_Locked();
- if (entry->runningReader_Locked()) {
- LogTimeEntry::unlock();
- return;
- }
- entry->incRef_Locked();
- break;
+ LogTimeEntry::unlock();
+ return;
}
it++;
}
- if (it == times.end()) {
- // Create LogTimeEntry in notifyNewLog() ?
- if (mTail == (unsigned long)-1) {
- LogTimeEntry::unlock();
- return;
- }
- entry = new LogTimeEntry(mReader, client, mNonBlock, mTail, mLogMask,
- mPid, mStart, mTimeout);
- times.push_front(entry);
- }
-
- client->incRef();
-
- // release client and entry reference counts once done
- entry->startReader_Locked();
LogTimeEntry::unlock();
}
diff --git a/logd/FlushCommand.h b/logd/FlushCommand.h
index 543dfc3..ceaf393 100644
--- a/logd/FlushCommand.h
+++ b/logd/FlushCommand.h
@@ -27,36 +27,11 @@
class FlushCommand : public SocketClientCommand {
LogReader& mReader;
- bool mNonBlock;
- unsigned long mTail;
log_mask_t mLogMask;
- pid_t mPid;
- log_time mStart;
- uint64_t mTimeout;
public:
- // for opening a reader
- explicit FlushCommand(LogReader& reader, bool nonBlock, unsigned long tail,
- log_mask_t logMask, pid_t pid, log_time start,
- uint64_t timeout)
- : mReader(reader),
- mNonBlock(nonBlock),
- mTail(tail),
- mLogMask(logMask),
- mPid(pid),
- mStart(start),
- mTimeout((start != log_time::EPOCH) ? timeout : 0) {
- }
-
- // for notification of an update
explicit FlushCommand(LogReader& reader, log_mask_t logMask)
- : mReader(reader),
- mNonBlock(false),
- mTail(-1),
- mLogMask(logMask),
- mPid(0),
- mStart(log_time::EPOCH),
- mTimeout(0) {
+ : mReader(reader), mLogMask(logMask) {
}
virtual void runSocketCommand(SocketClient* client);
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index fd1b8b2..fbdbf79 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -105,10 +105,8 @@
LastLogTimes::iterator times = mTimes.begin();
while (times != mTimes.end()) {
- LogTimeEntry* entry = (*times);
- if (entry->owned_Locked()) {
- entry->triggerReader_Locked();
- }
+ LogTimeEntry* entry = times->get();
+ entry->triggerReader_Locked();
times++;
}
@@ -409,17 +407,15 @@
LastLogTimes::iterator times = mTimes.begin();
while (times != mTimes.end()) {
- LogTimeEntry* entry = (*times);
- if (entry->owned_Locked()) {
- if (!entry->mNonBlock) {
- end_always = true;
- break;
- }
- // it passing mEnd is blocked by the following checks.
- if (!end_set || (end <= entry->mEnd)) {
- end = entry->mEnd;
- end_set = true;
- }
+ LogTimeEntry* entry = times->get();
+ if (!entry->mNonBlock) {
+ end_always = true;
+ break;
+ }
+ // it passing mEnd is blocked by the following checks.
+ if (!end_set || (end <= entry->mEnd)) {
+ end = entry->mEnd;
+ end_set = true;
}
times++;
}
@@ -710,8 +706,8 @@
// Region locked?
LastLogTimes::iterator times = mTimes.begin();
while (times != mTimes.end()) {
- LogTimeEntry* entry = (*times);
- if (entry->owned_Locked() && entry->isWatching(id) &&
+ LogTimeEntry* entry = times->get();
+ if (entry->isWatching(id) &&
(!oldest || (oldest->mStart > entry->mStart) ||
((oldest->mStart == entry->mStart) &&
(entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec)))) {
@@ -1052,9 +1048,9 @@
LogTimeEntry::wrlock();
LastLogTimes::iterator times = mTimes.begin();
while (times != mTimes.end()) {
- LogTimeEntry* entry = (*times);
+ LogTimeEntry* entry = times->get();
// Killer punch
- if (entry->owned_Locked() && entry->isWatching(id)) {
+ if (entry->isWatching(id)) {
entry->release_Locked();
}
times++;
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index 2b6556d..13c7af3 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -41,6 +41,7 @@
runOnEachSocket(&command);
}
+// Note returning false will release the SocketClient instance.
bool LogReader::onDataAvailable(SocketClient* cli) {
static bool name_set;
if (!name_set) {
@@ -57,6 +58,18 @@
}
buffer[len] = '\0';
+ // Clients are only allowed to send one command, disconnect them if they
+ // send another.
+ LogTimeEntry::wrlock();
+ for (const auto& entry : mLogbuf.mTimes) {
+ if (entry->mClient == cli) {
+ entry->release_Locked();
+ LogTimeEntry::unlock();
+ return false;
+ }
+ }
+ LogTimeEntry::unlock();
+
unsigned long tail = 0;
static const char _tail[] = " tail=";
char* cp = strstr(buffer, _tail);
@@ -199,14 +212,25 @@
cli->getUid(), cli->getGid(), cli->getPid(), nonBlock ? 'n' : 'b', tail,
logMask, (int)pid, sequence.nsec(), timeout);
- FlushCommand command(*this, nonBlock, tail, logMask, pid, sequence, timeout);
+ LogTimeEntry::wrlock();
+ auto entry = std::make_unique<LogTimeEntry>(
+ *this, cli, nonBlock, tail, logMask, pid, sequence, timeout);
+ if (!entry->startReader_Locked()) {
+ LogTimeEntry::unlock();
+ return false;
+ }
+
+ // release client and entry reference counts once done
+ cli->incRef();
+ mLogbuf.mTimes.emplace_front(std::move(entry));
// Set acceptable upper limit to wait for slow reader processing b/27242723
struct timeval t = { LOGD_SNDTIMEO, 0 };
setsockopt(cli->getSocket(), SOL_SOCKET, SO_SNDTIMEO, (const char*)&t,
sizeof(t));
- command.runSocketCommand(cli);
+ LogTimeEntry::unlock();
+
return true;
}
@@ -215,9 +239,8 @@
LogTimeEntry::wrlock();
LastLogTimes::iterator it = times.begin();
while (it != times.end()) {
- LogTimeEntry* entry = (*it);
+ LogTimeEntry* entry = it->get();
if (entry->mClient == cli) {
- times.erase(it);
entry->release_Locked();
break;
}
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index 7a6f84b..1715501 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -30,11 +30,7 @@
LogTimeEntry::LogTimeEntry(LogReader& reader, SocketClient* client,
bool nonBlock, unsigned long tail, log_mask_t logMask,
pid_t pid, log_time start, uint64_t timeout)
- : mRefCount(1),
- mRelease(false),
- mError(false),
- threadRunning(false),
- leadingDropped(false),
+ : leadingDropped(false),
mReader(reader),
mLogMask(logMask),
mPid(pid),
@@ -52,65 +48,21 @@
cleanSkip_Locked();
}
-void LogTimeEntry::startReader_Locked(void) {
+bool LogTimeEntry::startReader_Locked() {
pthread_attr_t attr;
- threadRunning = true;
-
if (!pthread_attr_init(&attr)) {
if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
if (!pthread_create(&mThread, &attr, LogTimeEntry::threadStart,
this)) {
pthread_attr_destroy(&attr);
- return;
+ return true;
}
}
pthread_attr_destroy(&attr);
}
- threadRunning = false;
- if (mClient) {
- mClient->decRef();
- }
- decRef_Locked();
-}
-void LogTimeEntry::threadStop(void* obj) {
- LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
-
- wrlock();
-
- if (me->mNonBlock) {
- me->error_Locked();
- }
-
- SocketClient* client = me->mClient;
-
- if (me->isError_Locked()) {
- LogReader& reader = me->mReader;
- LastLogTimes& times = reader.logbuf().mTimes;
-
- LastLogTimes::iterator it = times.begin();
- while (it != times.end()) {
- if (*it == me) {
- times.erase(it);
- me->release_nodelete_Locked();
- break;
- }
- it++;
- }
-
- me->mClient = nullptr;
- reader.release(client);
- }
-
- if (client) {
- client->decRef();
- }
-
- me->threadRunning = false;
- me->decRef_Locked();
-
- unlock();
+ return false;
}
void* LogTimeEntry::threadStart(void* obj) {
@@ -118,13 +70,7 @@
LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
- pthread_cleanup_push(threadStop, obj);
-
SocketClient* client = me->mClient;
- if (!client) {
- me->error();
- return nullptr;
- }
LogBuffer& logbuf = me->mReader.logbuf();
@@ -137,14 +83,14 @@
log_time start = me->mStart;
- while (me->threadRunning && !me->isError_Locked()) {
+ while (!me->mRelease) {
if (me->mTimeout.tv_sec || me->mTimeout.tv_nsec) {
if (pthread_cond_timedwait(&me->threadTriggeredCondition,
×Lock, &me->mTimeout) == ETIMEDOUT) {
me->mTimeout.tv_sec = 0;
me->mTimeout.tv_nsec = 0;
}
- if (!me->threadRunning || me->isError_Locked()) {
+ if (me->mRelease) {
break;
}
}
@@ -162,13 +108,12 @@
wrlock();
if (start == LogBufferElement::FLUSH_ERROR) {
- me->error_Locked();
break;
}
me->mStart = start + log_time(0, 1);
- if (me->mNonBlock || !me->threadRunning || me->isError_Locked()) {
+ if (me->mNonBlock || me->mRelease) {
break;
}
@@ -179,9 +124,21 @@
}
}
- unlock();
+ LogReader& reader = me->mReader;
+ reader.release(client);
- pthread_cleanup_pop(true);
+ client->decRef();
+
+ LastLogTimes& times = reader.logbuf().mTimes;
+ auto it =
+ std::find_if(times.begin(), times.end(),
+ [&me](const auto& other) { return other.get() == me; });
+
+ if (it != times.end()) {
+ times.erase(it);
+ }
+
+ unlock();
return nullptr;
}
@@ -247,10 +204,6 @@
goto skip;
}
- if (me->isError_Locked()) {
- goto stop;
- }
-
if (!me->mTail) {
goto ok;
}
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index 76d016c..f4e165f 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -22,6 +22,7 @@
#include <time.h>
#include <list>
+#include <memory>
#include <log/log.h>
#include <sysutils/SocketClient.h>
@@ -33,16 +34,12 @@
class LogTimeEntry {
static pthread_mutex_t timesLock;
- unsigned int mRefCount;
- bool mRelease;
- bool mError;
- bool threadRunning;
+ bool mRelease = false;
bool leadingDropped;
pthread_cond_t threadTriggeredCondition;
pthread_t mThread;
LogReader& mReader;
static void* threadStart(void* me);
- static void threadStop(void* me);
const log_mask_t mLogMask;
const pid_t mPid;
unsigned int skipAhead[LOG_ID_MAX];
@@ -73,11 +70,8 @@
pthread_mutex_unlock(×Lock);
}
- void startReader_Locked(void);
+ bool startReader_Locked();
- bool runningReader_Locked(void) const {
- return threadRunning || mRelease || mError || mNonBlock;
- }
void triggerReader_Locked(void) {
pthread_cond_signal(&threadTriggeredCondition);
}
@@ -87,54 +81,11 @@
}
void cleanSkip_Locked(void);
- // These called after LogTimeEntry removed from list, lock implicitly held
- void release_nodelete_Locked(void) {
- mRelease = true;
- pthread_cond_signal(&threadTriggeredCondition);
- // assumes caller code path will call decRef_Locked()
- }
-
void release_Locked(void) {
mRelease = true;
pthread_cond_signal(&threadTriggeredCondition);
- if (mRefCount || threadRunning) {
- return;
- }
- // No one else is holding a reference to this
- delete this;
}
- // Called to mark socket in jeopardy
- void error_Locked(void) {
- mError = true;
- }
- void error(void) {
- wrlock();
- error_Locked();
- unlock();
- }
-
- bool isError_Locked(void) const {
- return mRelease || mError;
- }
-
- // Mark Used
- // Locking implied, grabbed when protection around loop iteration
- void incRef_Locked(void) {
- ++mRefCount;
- }
-
- bool owned_Locked(void) const {
- return mRefCount != 0;
- }
-
- void decRef_Locked(void) {
- if ((mRefCount && --mRefCount) || !mRelease || threadRunning) {
- return;
- }
- // No one else is holding a reference to this
- delete this;
- }
bool isWatching(log_id_t id) const {
return mLogMask & (1 << id);
}
@@ -146,6 +97,6 @@
static int FilterSecondPass(const LogBufferElement* element, void* me);
};
-typedef std::list<LogTimeEntry*> LastLogTimes;
+typedef std::list<std::unique_ptr<LogTimeEntry>> LastLogTimes;
#endif // _LOGD_LOG_TIMES_H__
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index a4c3955..e4e1650 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -67,6 +67,13 @@
LOCAL_REQUIRED_MODULES := asan.options $(ASAN_OPTIONS_FILES) $(ASAN_EXTRACT_FILES)
endif
+EXPORT_GLOBAL_HWASAN_OPTIONS :=
+ifneq ($(filter hwaddress,$(SANITIZE_TARGET)),)
+ ifneq ($(HWADDRESS_SANITIZER_GLOBAL_OPTIONS),)
+ EXPORT_GLOBAL_HWASAN_OPTIONS := export HWASAN_OPTIONS $(HWADDRESS_SANITIZER_GLOBAL_OPTIONS)
+ endif
+endif
+
EXPORT_GLOBAL_GCOV_OPTIONS :=
ifeq ($(NATIVE_COVERAGE),true)
EXPORT_GLOBAL_GCOV_OPTIONS := export GCOV_PREFIX /data/misc/gcov
@@ -151,6 +158,7 @@
$(hide) sed -i -e 's?%SYSTEMSERVERCLASSPATH%?$(PRODUCT_SYSTEM_SERVER_CLASSPATH)?g' $@
$(hide) sed -i -e 's?%EXPORT_GLOBAL_ASAN_OPTIONS%?$(EXPORT_GLOBAL_ASAN_OPTIONS)?g' $@
$(hide) sed -i -e 's?%EXPORT_GLOBAL_GCOV_OPTIONS%?$(EXPORT_GLOBAL_GCOV_OPTIONS)?g' $@
+ $(hide) sed -i -e 's?%EXPORT_GLOBAL_HWASAN_OPTIONS%?$(EXPORT_GLOBAL_HWASAN_OPTIONS)?g' $@
bcp_md5 :=
bcp_dep :=
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index 2e2ab74..4576776 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -11,3 +11,4 @@
export SYSTEMSERVERCLASSPATH %SYSTEMSERVERCLASSPATH%
%EXPORT_GLOBAL_ASAN_OPTIONS%
%EXPORT_GLOBAL_GCOV_OPTIONS%
+ %EXPORT_GLOBAL_HWASAN_OPTIONS%