VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/dbg/dbgstackdumpself.cpp

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.9 KB
Line 
1/* $Id: dbgstackdumpself.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * IPRT - Dump current thread stack to buffer.
4 */
5
6/*
7 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include "internal/iprt.h"
42#include <iprt/dbg.h>
43
44#include <iprt/errcore.h>
45#include <iprt/string.h>
46#include <iprt/ldr.h>
47#include <iprt/list.h>
48#include <iprt/mem.h>
49#include <iprt/path.h>
50#include <iprt/string.h>
51
52#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
53# include <iprt/x86.h>
54#else
55# error "PORTME"
56#endif
57
58#ifdef RT_OS_WINDOWS
59# include <iprt/param.h>
60# include <iprt/utf16.h>
61# include <iprt/win/windows.h>
62#elif defined(RT_OS_LINUX) || defined(RT_OS_DARWIN)
63# include <dlfcn.h>
64#endif
65
66
67/*********************************************************************************************************************************
68* Structures and Typedefs *
69*********************************************************************************************************************************/
70/**
71 * Cached module.
72 */
73typedef struct RTDBGSTACKSELFMOD
74{
75 /** List entry. */
76 RTLISTNODE ListEntry;
77 /** The mapping address. */
78 uintptr_t uMapping;
79 /** The size of the mapping. */
80 size_t cbMapping;
81 /** The loader module handle. */
82 RTLDRMOD hLdrMod;
83 /** The debug module handle, if available. */
84 RTDBGMOD hDbgMod;
85 /** Offset into szFilename of the name part. */
86 size_t offName;
87 /** the module filename. */
88 char szFilename[RTPATH_MAX];
89} RTDBGSTACKSELFMOD;
90/** Pointer to a cached module. */
91typedef RTDBGSTACKSELFMOD *PRTDBGSTACKSELFMOD;
92
93
94/**
95 * Symbol search state.
96 */
97typedef struct RTDBGSTACKSELFSYMSEARCH
98{
99 /** The address (not RVA) we're searching for a symbol for. */
100 uintptr_t uSearch;
101 /** The distance of the current hit. This is ~(uintptr_t)0 if no hit. */
102 uintptr_t offBestDist;
103 /** Where to return symbol information. */
104 PRTDBGSYMBOL pSymInfo;
105} RTDBGSTACKSELFSYMSEARCH;
106typedef RTDBGSTACKSELFSYMSEARCH *PRTDBGSTACKSELFSYMSEARCH;
107
108
109/*********************************************************************************************************************************
110* Internal Functions *
111*********************************************************************************************************************************/
112/* Wanted to use DECLHIDDEN(DECLASM(size_t)) here, but early solaris 11 doesn't like it. */
113RT_C_DECLS_BEGIN
114DECL_HIDDEN_CALLBACK(size_t) rtDbgStackDumpSelfWorker(char *pszStack, size_t cbStack, uint32_t fFlags, PCRTCCUINTREG pauRegs);
115RT_C_DECLS_END
116
117
118/**
119 * Worker for stack and module reader callback.
120 *
121 * @returns IPRT status code
122 * @param pvDst Where to put the request memory.
123 * @param cbToRead How much to read.
124 * @param uSrcAddr Where to read the memory from.
125 */
126static int rtDbgStackDumpSelfSafeMemoryReader(void *pvDst, size_t cbToRead, uintptr_t uSrcAddr)
127{
128# ifdef RT_OS_WINDOWS
129# if 1
130 __try
131 {
132 memcpy(pvDst, (void const *)uSrcAddr, cbToRead);
133 }
134 __except(EXCEPTION_EXECUTE_HANDLER)
135 {
136 return VERR_ACCESS_DENIED;
137 }
138# else
139 SIZE_T cbActual = 0;
140 if (ReadProcessMemory(GetCurrentProcess(), (void const *)uSrcAddr, pvDst, cbToRead, &cbActual))
141 {
142 if (cbActual >= cbToRead)
143 return VINF_SUCCESS;
144 memset((uint8_t *)pvDst + cbActual, 0, cbToRead - cbActual);
145 return VINF_SUCCESS;
146 }
147# endif
148# else
149 /** @todo secure this from SIGSEGV. */
150 memcpy(pvDst, (void const *)uSrcAddr, cbToRead);
151# endif
152 return VINF_SUCCESS;
153}
154
155
156#if defined(RT_OS_WINDOWS) && 0
157/**
158 * @callback_method_impl{FNRTLDRRDRMEMREAD}
159 */
160static DECLCALLBACK(int) rtDbgStackDumpSelfModReader(void *pvBuf, size_t cb, size_t off, void *pvUser)
161{
162 PRTDBGSTACKSELFMOD pMod = (PRTDBGSTACKSELFMOD)pvUser;
163 return rtDbgStackDumpSelfSafeMemoryReader(pvBuf, cb, pMod->uMapping + off);
164}
165#endif
166
167
168/**
169 * @interface_method_impl{RTDBGUNWINDSTATE,pfnReadStack}
170 */
171static DECLCALLBACK(int) rtDbgStackDumpSelfReader(PRTDBGUNWINDSTATE pThis, RTUINTPTR uSp, size_t cbToRead, void *pvDst)
172{
173 RT_NOREF(pThis);
174 return rtDbgStackDumpSelfSafeMemoryReader(pvDst, cbToRead, uSp);
175}
176
177
178#ifdef RT_OS_WINDOWS
179/**
180 * Figure the size of a loaded PE image.
181 * @returns The size.
182 * @param uBase The image base address.
183 */
184static size_t rtDbgStackDumpSelfGetPeImageSize(uintptr_t uBase)
185{
186 union
187 {
188 IMAGE_DOS_HEADER DosHdr;
189 IMAGE_NT_HEADERS NtHdrs;
190 } uBuf;
191 int rc = rtDbgStackDumpSelfSafeMemoryReader(&uBuf, sizeof(uBuf.DosHdr), uBase);
192 if (RT_SUCCESS(rc))
193 {
194 if ( uBuf.DosHdr.e_magic == IMAGE_DOS_SIGNATURE
195 && uBuf.DosHdr.e_lfanew < _2M)
196 {
197 rc = rtDbgStackDumpSelfSafeMemoryReader(&uBuf, sizeof(uBuf.NtHdrs), uBase + uBuf.DosHdr.e_lfanew);
198 if (RT_SUCCESS(rc))
199 {
200 if (uBuf.NtHdrs.Signature == IMAGE_NT_SIGNATURE)
201 return uBuf.NtHdrs.OptionalHeader.SizeOfImage;
202 }
203 }
204 }
205 return _64K;
206}
207#endif
208
209
210/**
211 * Works the module cache.
212 *
213 * @returns Pointer to module cache entry on success, NULL otherwise.
214 * @param uPc The PC to locate a module for.
215 * @param pCachedModules The module cache.
216 */
217static PRTDBGSTACKSELFMOD rtDbgStackDumpSelfQueryModForPC(uintptr_t uPc, PRTLISTANCHOR pCachedModules)
218{
219 /*
220 * Search the list first.
221 */
222 PRTDBGSTACKSELFMOD pMod;
223 RTListForEach(pCachedModules, pMod, RTDBGSTACKSELFMOD, ListEntry)
224 {
225 if (uPc - pMod->uMapping < pMod->cbMapping)
226 return pMod;
227 }
228
229 /*
230 * Try figure out the module using the native loader or similar.
231 */
232#ifdef RT_OS_WINDOWS
233 HMODULE hmod = NULL;
234 static bool volatile s_fResolvedSymbols = false;
235 static decltype(GetModuleHandleExW) *g_pfnGetModuleHandleExW = NULL;
236 decltype(GetModuleHandleExW) *pfnGetModuleHandleExW;
237 if (s_fResolvedSymbols)
238 pfnGetModuleHandleExW = g_pfnGetModuleHandleExW;
239 else
240 {
241 pfnGetModuleHandleExW = (decltype(GetModuleHandleExW) *)GetProcAddress(GetModuleHandleW(L"kernel32.dll"),
242 "GetModuleHandleExW");
243 g_pfnGetModuleHandleExW = pfnGetModuleHandleExW;
244 s_fResolvedSymbols = true;
245 }
246 if ( pfnGetModuleHandleExW
247 && pfnGetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
248 (LPCWSTR)uPc, &hmod))
249 {
250 WCHAR wszFilename[RTPATH_MAX];
251 DWORD cwcFilename = GetModuleFileNameW(hmod, wszFilename, RT_ELEMENTS(wszFilename));
252 if (cwcFilename > 0)
253 {
254 pMod = (PRTDBGSTACKSELFMOD)RTMemAllocZ(sizeof(*pMod));
255 if (pMod)
256 {
257 char *pszDst = pMod->szFilename;
258 int rc = RTUtf16ToUtf8Ex(wszFilename, cwcFilename, &pszDst, sizeof(pMod->szFilename), NULL);
259 if (RT_SUCCESS(rc))
260 {
261 const char *pszFilename = RTPathFilename(pMod->szFilename);
262 pMod->offName = pszFilename ? pszFilename - &pMod->szFilename[0] : 0;
263 pMod->uMapping = (uintptr_t)hmod & ~(uintptr_t)(PAGE_SIZE - 1);
264 pMod->cbMapping = rtDbgStackDumpSelfGetPeImageSize(pMod->uMapping);
265 pMod->hLdrMod = NIL_RTLDRMOD;
266 pMod->hDbgMod = NIL_RTDBGMOD;
267
268# if 0 /* this ain't reliable, trouble enumerate symbols in VBoxRT. But why bother when we can load it off the disk. */
269 rc = RTLdrOpenInMemory(&pMod->szFilename[pMod->offName], RTLDR_O_FOR_DEBUG, RTLdrGetHostArch(),
270 pMod->cbMapping, rtDbgStackDumpSelfModReader, NULL /*pfnDtor*/, pMod /*pvUser*/,
271 &pMod->hLdrMod, NULL /*pErrInfo*/);
272# else
273 rc = RTLdrOpen(pMod->szFilename, RTLDR_O_FOR_DEBUG, RTLdrGetHostArch(), &pMod->hLdrMod);
274# endif
275 if (RT_SUCCESS(rc))
276 {
277 pMod->cbMapping = RTLdrSize(pMod->hLdrMod);
278
279 /* Try open debug info for the module. */
280 uint32_t uTimeDateStamp = 0;
281 RTLdrQueryProp(pMod->hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &uTimeDateStamp, sizeof(uTimeDateStamp));
282 rc = RTDbgModCreateFromPeImage(&pMod->hDbgMod, pMod->szFilename, &pMod->szFilename[pMod->offName],
283 &pMod->hLdrMod, (uint32_t)pMod->cbMapping, uTimeDateStamp, NIL_RTDBGCFG);
284 RTListPrepend(pCachedModules, &pMod->ListEntry);
285 return pMod;
286 }
287 }
288 RTMemFree(pMod);
289 }
290 }
291 }
292#elif defined(RT_OS_LINUX) || defined(RT_OS_DARWIN)
293 Dl_info Info = { NULL, NULL, NULL, NULL };
294 int rc = dladdr((const void *)uPc, &Info);
295 if (rc != 0 && Info.dli_fname)
296 {
297 pMod = (PRTDBGSTACKSELFMOD)RTMemAllocZ(sizeof(*pMod));
298 if (pMod)
299 {
300 /** @todo better filename translation... */
301 rc = RTStrCopy(pMod->szFilename, sizeof(pMod->szFilename), Info.dli_fname);
302 if (RT_SUCCESS(rc))
303 {
304 RTStrPurgeEncoding(pMod->szFilename);
305
306 const char *pszFilename = RTPathFilename(pMod->szFilename);
307 pMod->offName = pszFilename ? pszFilename - &pMod->szFilename[0] : 0;
308 pMod->uMapping = (uintptr_t)Info.dli_fbase;
309 pMod->cbMapping = 0;
310 pMod->hLdrMod = NIL_RTLDRMOD;
311 pMod->hDbgMod = NIL_RTDBGMOD;
312
313 rc = RTLdrOpen(pMod->szFilename, RTLDR_O_FOR_DEBUG, RTLdrGetHostArch(), &pMod->hLdrMod);
314 if (RT_SUCCESS(rc))
315 {
316 pMod->cbMapping = RTLdrSize(pMod->hLdrMod);
317
318 /* Try open debug info for the module. */
319 //uint32_t uTimeDateStamp = 0;
320 //RTLdrQueryProp(pMod->hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &uTimeDateStamp, sizeof(uTimeDateStamp));
321 //RTDbgModCreateFromImage()??
322 //rc = RTDbgModCreateFromPeImage(&pMod->hDbgMod, pMod->szFilename, &pMod->szFilename[pMod->offName],
323 // &pMod->hLdrMod, (uint32_t)pMod->cbMapping, uTimeDateStamp, NIL_RTDBGCFG);
324
325 RTListPrepend(pCachedModules, &pMod->ListEntry);
326 return pMod;
327 }
328 }
329 RTMemFree(pMod);
330 }
331 }
332#endif
333 return NULL;
334}
335
336
337/**
338 * @callback_method_impl{FNRTLDRENUMSYMS}
339 */
340static DECLCALLBACK(int) rtDbgStackdumpSelfSymbolSearchCallback(RTLDRMOD hLdrMod, const char *pszSymbol,
341 unsigned uSymbol, RTLDRADDR Value, void *pvUser)
342{
343 PRTDBGSTACKSELFSYMSEARCH pSearch = (PRTDBGSTACKSELFSYMSEARCH)pvUser;
344 if (Value >= pSearch->uSearch)
345 {
346 uintptr_t const offDist = (uintptr_t)Value - pSearch->uSearch;
347 if (offDist < pSearch->offBestDist)
348 {
349 pSearch->offBestDist = offDist;
350
351 PRTDBGSYMBOL pSymInfo = pSearch->pSymInfo;
352 pSymInfo->Value = Value;
353 pSymInfo->offSeg = Value;
354 pSymInfo->iSeg = RTDBGSEGIDX_ABS;
355 pSymInfo->iOrdinal = uSymbol;
356 pSymInfo->fFlags = 0;
357 if (pszSymbol)
358 RTStrCopy(pSymInfo->szName, sizeof(pSymInfo->szName), pszSymbol);
359 else
360 RTStrPrintf(pSymInfo->szName, sizeof(pSymInfo->szName), "Ordinal#%u", uSymbol);
361
362 if (offDist < 8)
363 return VINF_CALLBACK_RETURN;
364 }
365 }
366 RT_NOREF(hLdrMod);
367 return VINF_SUCCESS;
368}
369
370
371/**
372 * Searches for a symbol close to @a uRva.
373 *
374 * @returns true if found, false if not.
375 * @param pMod The module cache entry to search in.
376 * @param uRva The RVA to locate a symbol for.
377 * @param poffDisp Where to return the distance between uRva and the returned symbol.
378 * @param pSymInfo Where to return the symbol information.
379 */
380static bool rtDbgStackDumpSelfQuerySymbol(PRTDBGSTACKSELFMOD pMod, uintptr_t uRva, PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo)
381{
382 if (pMod->hDbgMod != NIL_RTDBGMOD)
383 {
384 int rc = RTDbgModSymbolByAddr(pMod->hDbgMod, RTDBGSEGIDX_RVA, uRva, RTDBGSYMADDR_FLAGS_LESS_OR_EQUAL,
385 poffDisp, pSymInfo);
386 if (RT_SUCCESS(rc))
387 return true;
388 }
389
390 if (pMod->hLdrMod != NIL_RTLDRMOD)
391 {
392 RTDBGSTACKSELFSYMSEARCH SearchInfo = { pMod->uMapping + uRva, ~(uintptr_t)0, pSymInfo };
393 int rc = RTLdrEnumSymbols(pMod->hLdrMod, 0, (const void *)pMod->uMapping, pMod->uMapping,
394 rtDbgStackdumpSelfSymbolSearchCallback, &SearchInfo);
395 if (RT_SUCCESS(rc) && SearchInfo.offBestDist != ~(uintptr_t)0)
396 {
397 *poffDisp = SearchInfo.offBestDist;
398 return true;
399 }
400 }
401
402 return false;
403}
404
405
406/**
407 * Does the grunt work for RTDbgStackDumpSelf.
408 *
409 * Called thru an assembly wrapper that collects the necessary register state.
410 *
411 * @returns Length of the string returned in pszStack.
412 * @param pszStack Where to output the stack dump.
413 * @param cbStack The size of the @a pszStack buffer.
414 * @param fFlags Flags, MBZ.
415 * @param pauRegs Register state. For AMD64 and x86 this starts with the
416 * PC and us followed by the general purpose registers.
417 */
418DECL_HIDDEN_CALLBACK(size_t) rtDbgStackDumpSelfWorker(char *pszStack, size_t cbStack, uint32_t fFlags, PCRTCCUINTREG pauRegs)
419{
420 RT_NOREF(fFlags);
421
422 /*
423 * Initialize the unwind state.
424 */
425 RTDBGUNWINDSTATE UnwindState;
426 RT_ZERO(UnwindState);
427
428 UnwindState.u32Magic = RTDBGUNWINDSTATE_MAGIC;
429 UnwindState.pfnReadStack = rtDbgStackDumpSelfReader;
430#ifdef RT_ARCH_AMD64
431 UnwindState.enmArch = RTLDRARCH_AMD64;
432 UnwindState.uPc = pauRegs[0];
433 UnwindState.enmRetType = RTDBGRETURNTYPE_NEAR64;
434 for (unsigned i = 0; i < 16; i++)
435 UnwindState.u.x86.auRegs[i] = pauRegs[i + 1];
436#elif defined(RT_ARCH_X86)
437 UnwindState.enmArch = RTLDRARCH_X86_32;
438 UnwindState.uPc = pauRegs[0];
439 UnwindState.enmRetType = RTDBGRETURNTYPE_NEAR32;
440 for (unsigned i = 0; i < 8; i++)
441 UnwindState.u.x86.auRegs[i] = pauRegs[i + 1];
442#else
443# error "PORTME"
444#endif
445
446 /*
447 * We cache modules.
448 */
449 PRTDBGSTACKSELFMOD pCurModule = NULL;
450 RTLISTANCHOR CachedModules;
451 RTListInit(&CachedModules);
452
453 /*
454 * Work the stack.
455 */
456 size_t offDst = 0;
457 while (offDst + 64 < cbStack)
458 {
459 /* Try locate the module containing the current PC. */
460 if ( !pCurModule
461 || UnwindState.uPc - pCurModule->uMapping >= pCurModule->cbMapping)
462 pCurModule = rtDbgStackDumpSelfQueryModForPC(UnwindState.uPc, &CachedModules);
463 bool fManualUnwind = true;
464 if (!pCurModule)
465 offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, "%p\n", UnwindState.uPc);
466 else
467 {
468 uintptr_t const uRva = UnwindState.uPc - pCurModule->uMapping;
469
470 /*
471 * Add a call stack entry with the symbol if we can.
472 */
473 union
474 {
475 RTDBGSYMBOL SymbolInfo;
476 RTDBGLINE LineInfo;
477 } uBuf;
478 RTINTPTR offDisp = 0;
479 if (!rtDbgStackDumpSelfQuerySymbol(pCurModule, uRva, &offDisp, &uBuf.SymbolInfo))
480 offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, "%p %s + %#zx\n",
481 UnwindState.uPc, &pCurModule->szFilename[pCurModule->offName], (size_t)uRva);
482 else if (offDisp == 0)
483 offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, "%p %s!%s (rva:%#zx)\n", UnwindState.uPc,
484 &pCurModule->szFilename[pCurModule->offName], uBuf.SymbolInfo.szName, (size_t)uRva);
485 else
486 offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, "%p %s!%s%c%#zx (rva:%#zx)\n",
487 UnwindState.uPc, &pCurModule->szFilename[pCurModule->offName], uBuf.SymbolInfo.szName,
488 offDisp >= 0 ? '+' : '-', (size_t)RT_ABS(offDisp), (size_t)uRva);
489
490 /*
491 * Try supply the line number.
492 */
493 if (pCurModule->hDbgMod != NIL_RTDBGMOD)
494 {
495 offDisp = 0;
496 int rc = RTDbgModLineByAddr(pCurModule->hDbgMod, RTDBGSEGIDX_RVA, uRva, &offDisp, &uBuf.LineInfo);
497 if (RT_SUCCESS(rc) && offDisp)
498 offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, " [%s:%u]\n",
499 uBuf.LineInfo.szFilename, uBuf.LineInfo.uLineNo);
500 else if (RT_SUCCESS(rc))
501 offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, " [%s:%u (%c%#zx)]\n", uBuf.LineInfo.szFilename,
502 uBuf.LineInfo.uLineNo, offDisp >= 0 ? '+' : '-', (size_t)RT_ABS(offDisp));
503 }
504
505 /*
506 * Try unwind using the module info.
507 */
508 int rc;
509 if (pCurModule->hDbgMod != NIL_RTDBGMOD)
510 rc = RTDbgModUnwindFrame(pCurModule->hDbgMod, RTDBGSEGIDX_RVA, uRva, &UnwindState);
511 else
512 rc = RTLdrUnwindFrame(pCurModule->hLdrMod, (void const *)pCurModule->uMapping, UINT32_MAX, uRva, &UnwindState);
513 if (RT_SUCCESS(rc))
514 fManualUnwind = false;
515 }
516 if (fManualUnwind)
517 {
518 break;
519 }
520 }
521
522 /*
523 * Destroy the cache.
524 */
525 PRTDBGSTACKSELFMOD pNextModule;
526 RTListForEachSafe(&CachedModules, pCurModule, pNextModule, RTDBGSTACKSELFMOD, ListEntry)
527 {
528 if (pCurModule->hDbgMod != NIL_RTDBGMOD)
529 {
530 RTDbgModRelease(pCurModule->hDbgMod);
531 pCurModule->hDbgMod = NIL_RTDBGMOD;
532 }
533 if (pCurModule->hLdrMod != NIL_RTLDRMOD)
534 {
535 RTLdrClose(pCurModule->hLdrMod);
536 pCurModule->hLdrMod = NIL_RTLDRMOD;
537 }
538 RTMemFree(pCurModule);
539 }
540
541 return offDst;
542}
543
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette