VirtualBox

Changeset 72778 in vbox for trunk/src/VBox/VMM


Ignore:
Timestamp:
Jun 29, 2018 8:02:35 PM (7 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
123320
Message:

VMM,SUPDrv,IPRT: Redirect ring-0 release logging on EMTs to VBox.log.

  • VMM: Added release ring-0 logging on EMTs that writes to VBox.log.
  • IPRT: Made 'msprog' and 'timeprog' work for ring-0 logging on EMTs.
  • IPRT: Removed RTLogSetCustomPrefixCallbackForR0.
  • VMM: Removed vmmR0LoggerPrefix that was assoicated with the above API.
  • IPRT Fixed missing log prefix on the first log line.
  • SUPDrv: Turns out we require actual code for SUPR0GetDefaultLogRelInstanceEx and SUPR0GetDefaultLogInstanceEx.

Note! Requires updating the support driver.

Location:
trunk/src/VBox/VMM
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/VMM/VMMR0/GVMMR0.cpp

    r72428 r72778  
    19131913     * Search the handles in a linear fashion as we don't dare to take the lock (assert).
    19141914     */
     1915/** @todo introduce some pid hash table here, please. */
    19151916    for (unsigned i = 1; i < RT_ELEMENTS(pGVMM->aHandles); i++)
    19161917    {
     
    19271928            PGVM pGVM = pGVMM->aHandles[i].pGVM;
    19281929            VMCPUID const cCpus = pGVM->cCpus;
     1930            ASMCompilerBarrier();
    19291931            if (    cCpus < 1
    19301932                ||  cCpus > VMM_MAX_CPU_COUNT)
     
    19331935                if (pGVM->aCpus[idCpu].hEMT == hEMT)
    19341936                    return pGVMM->aHandles[i].pVM;
     1937        }
     1938    }
     1939    return NULL;
     1940}
     1941
     1942
     1943/**
     1944 * Looks up the GVMCPU belonging to the specified EMT thread.
     1945 *
     1946 * This is used by the assertion machinery in VMMR0.cpp to avoid causing
     1947 * unnecessary kernel panics when the EMT thread hits an assertion. The
     1948 * call may or not be an EMT thread.
     1949 *
     1950 * @returns Pointer to the VM on success, NULL on failure.
     1951 * @param   hEMT    The native thread handle of the EMT.
     1952 *                  NIL_RTNATIVETHREAD means the current thread
     1953 */
     1954GVMMR0DECL(PGVMCPU) GVMMR0GetGVCpuByEMT(RTNATIVETHREAD hEMT)
     1955{
     1956    /*
     1957     * No Assertions here as we're usually called in a AssertMsgN,
     1958     * RTAssert*, Log and LogRel contexts.
     1959     */
     1960    PGVMM pGVMM = g_pGVMM;
     1961    if (   !VALID_PTR(pGVMM)
     1962        || pGVMM->u32Magic != GVMM_MAGIC)
     1963        return NULL;
     1964
     1965    if (hEMT == NIL_RTNATIVETHREAD)
     1966        hEMT = RTThreadNativeSelf();
     1967    RTPROCESS ProcId = RTProcSelf();
     1968
     1969    /*
     1970     * Search the handles in a linear fashion as we don't dare to take the lock (assert).
     1971     */
     1972/** @todo introduce some pid hash table here, please. */
     1973    for (unsigned i = 1; i < RT_ELEMENTS(pGVMM->aHandles); i++)
     1974    {
     1975        if (   pGVMM->aHandles[i].iSelf == i
     1976            && pGVMM->aHandles[i].ProcId == ProcId
     1977            && VALID_PTR(pGVMM->aHandles[i].pvObj)
     1978            && VALID_PTR(pGVMM->aHandles[i].pVM)
     1979            && VALID_PTR(pGVMM->aHandles[i].pGVM))
     1980        {
     1981            PGVM pGVM = pGVMM->aHandles[i].pGVM;
     1982            if (pGVMM->aHandles[i].hEMT0 == hEMT)
     1983                return &pGVM->aCpus[0];
     1984
     1985            /* This is fearly safe with the current process per VM approach. */
     1986            VMCPUID const cCpus = pGVM->cCpus;
     1987            ASMCompilerBarrier();
     1988            ASMCompilerBarrier();
     1989            if (   cCpus < 1
     1990                || cCpus > VMM_MAX_CPU_COUNT)
     1991                continue;
     1992            for (VMCPUID idCpu = 1; idCpu < cCpus; idCpu++)
     1993                if (pGVM->aCpus[idCpu].hEMT == hEMT)
     1994                    return &pGVM->aCpus[idCpu];
    19351995        }
    19361996    }
  • trunk/src/VBox/VMM/VMMR0/VMMR0.cpp

    r72642 r72778  
    390390        return rc;
    391391
    392 
    393392#ifdef LOG_ENABLED
    394393    /*
     
    23452344}
    23462345
    2347 /**
    2348  * Internal R0 logger worker: Custom prefix.
    2349  *
    2350  * @returns Number of chars written.
    2351  *
    2352  * @param   pLogger     The logger instance.
    2353  * @param   pchBuf      The output buffer.
    2354  * @param   cchBuf      The size of the buffer.
    2355  * @param   pvUser      User argument (ignored).
    2356  */
    2357 VMMR0DECL(size_t) vmmR0LoggerPrefix(PRTLOGGER pLogger, char *pchBuf, size_t cchBuf, void *pvUser)
    2358 {
    2359     NOREF(pvUser);
    2360 #ifdef LOG_ENABLED
    2361     PVMMR0LOGGER pR0Logger = (PVMMR0LOGGER)((uintptr_t)pLogger - RT_OFFSETOF(VMMR0LOGGER, Logger));
    2362     if (    !VALID_PTR(pR0Logger)
    2363         ||  !VALID_PTR(pR0Logger + 1)
    2364         ||  pLogger->u32Magic != RTLOGGER_MAGIC
    2365         ||  cchBuf < 2)
    2366         return 0;
    2367 
    2368     static const char s_szHex[17] = "0123456789abcdef";
    2369     VMCPUID const     idCpu       = pR0Logger->idCpu;
    2370     pchBuf[1] = s_szHex[ idCpu       & 15];
    2371     pchBuf[0] = s_szHex[(idCpu >> 4) & 15];
    2372 
    2373     return 2;
    2374 #else
    2375     NOREF(pLogger); NOREF(pchBuf); NOREF(cchBuf);
    2376     return 0;
    2377 #endif
    2378 }
    2379 
    23802346#ifdef LOG_ENABLED
    23812347
     
    23892355    if (pVCpu->vmm.s.pR0LoggerR0)
    23902356        pVCpu->vmm.s.pR0LoggerR0->fFlushingDisabled = true;
     2357    if (pVCpu->vmm.s.pR0RelLoggerR0)
     2358        pVCpu->vmm.s.pR0RelLoggerR0->fFlushingDisabled = true;
    23912359}
    23922360
     
    24012369    if (pVCpu->vmm.s.pR0LoggerR0)
    24022370        pVCpu->vmm.s.pR0LoggerR0->fFlushingDisabled = false;
     2371    if (pVCpu->vmm.s.pR0RelLoggerR0)
     2372        pVCpu->vmm.s.pR0RelLoggerR0->fFlushingDisabled = false;
    24032373}
    24042374
     
    24132383    if (pVCpu->vmm.s.pR0LoggerR0)
    24142384        return pVCpu->vmm.s.pR0LoggerR0->fFlushingDisabled;
     2385    if (pVCpu->vmm.s.pR0RelLoggerR0)
     2386        return pVCpu->vmm.s.pR0RelLoggerR0->fFlushingDisabled;
    24152387    return true;
    24162388}
     2389
    24172390#endif /* LOG_ENABLED */
     2391
     2392/**
     2393 * Override RTLogRelGetDefaultInstanceEx so we can do LogRel to VBox.log from EMTs in ring-0.
     2394 */
     2395DECLEXPORT(PRTLOGGER) RTLogRelGetDefaultInstanceEx(uint32_t fFlagsAndGroup)
     2396{
     2397    PGVMCPU pGVCpu = GVMMR0GetGVCpuByEMT(NIL_RTNATIVETHREAD);
     2398    if (pGVCpu)
     2399    {
     2400        PVMCPU pVCpu = pGVCpu->pVCpu;
     2401        if (RT_VALID_PTR(pVCpu))
     2402        {
     2403            PVMMR0LOGGER pVmmLogger = pVCpu->vmm.s.pR0RelLoggerR0;
     2404            if (RT_VALID_PTR(pVmmLogger))
     2405            {
     2406                if (   pVmmLogger->fCreated
     2407                    && pVmmLogger->pVM == pGVCpu->pVM)
     2408                {
     2409                    if (pVmmLogger->Logger.fFlags & RTLOGFLAGS_DISABLED)
     2410                        return NULL;
     2411                    uint16_t const fFlags = RT_LO_U16(fFlagsAndGroup);
     2412                    uint16_t const iGroup = RT_HI_U16(fFlagsAndGroup);
     2413                    if (   iGroup != UINT16_MAX
     2414                        && (   (  pVmmLogger->Logger.afGroups[iGroup < pVmmLogger->Logger.cGroups ? iGroup : 0]
     2415                                & (fFlags | (uint32_t)RTLOGGRPFLAGS_ENABLED))
     2416                            != (fFlags | (uint32_t)RTLOGGRPFLAGS_ENABLED)))
     2417                        return NULL;
     2418                    return &pVmmLogger->Logger;
     2419                }
     2420            }
     2421        }
     2422    }
     2423    return SUPR0GetDefaultLogRelInstanceEx(fFlagsAndGroup);
     2424}
     2425
    24182426
    24192427/**
  • trunk/src/VBox/VMM/VMMR3/VM.cpp

    r72642 r72778  
    107107#endif
    108108static int                  vmR3InitDoCompleted(PVM pVM, VMINITCOMPLETED enmWhat);
    109 #ifdef LOG_ENABLED
    110 static DECLCALLBACK(size_t) vmR3LogPrefixCallback(PRTLOGGER pLogger, char *pchBuf, size_t cchBuf, void *pvUser);
    111 #endif
    112109static void                 vmR3DestroyUVM(PUVM pUVM, uint32_t cMilliesEMTWait);
    113110static bool                 vmR3ValidateStateTransition(VMSTATE enmStateOld, VMSTATE enmStateNew);
     
    709706                                         */
    710707                                        vmR3SetState(pVM, VMSTATE_CREATED, VMSTATE_CREATING);
    711 
    712 #ifdef LOG_ENABLED
    713                                         RTLogSetCustomPrefixCallback(NULL, vmR3LogPrefixCallback, pUVM);
    714 #endif
    715708                                        return VINF_SUCCESS;
    716709                                    }
     
    12181211
    12191212
    1220 #ifdef LOG_ENABLED
    1221 /**
    1222  * Logger callback for inserting a custom prefix.
    1223  *
    1224  * @returns Number of chars written.
    1225  * @param   pLogger             The logger.
    1226  * @param   pchBuf              The output buffer.
    1227  * @param   cchBuf              The output buffer size.
    1228  * @param   pvUser              Pointer to the UVM structure.
    1229  */
    1230 static DECLCALLBACK(size_t) vmR3LogPrefixCallback(PRTLOGGER pLogger, char *pchBuf, size_t cchBuf, void *pvUser)
    1231 {
    1232     AssertReturn(cchBuf >= 2, 0);
    1233     PUVM        pUVM   = (PUVM)pvUser;
    1234     PUVMCPU     pUVCpu = (PUVMCPU)RTTlsGet(pUVM->vm.s.idxTLS);
    1235     if (pUVCpu)
    1236     {
    1237         static const char s_szHex[17] = "0123456789abcdef";
    1238         VMCPUID const     idCpu       = pUVCpu->idCpu;
    1239         pchBuf[1] = s_szHex[ idCpu       & 15];
    1240         pchBuf[0] = s_szHex[(idCpu >> 4) & 15];
    1241     }
    1242     else
    1243     {
    1244         pchBuf[0] = 'x';
    1245         pchBuf[1] = 'y';
    1246     }
    1247 
    1248     NOREF(pLogger);
    1249     return 2;
    1250 }
    1251 #endif /* LOG_ENABLED */
    1252 
    1253 
    12541213/**
    12551214 * Calls the relocation functions for all VMM components so they can update
     
    27492708     * Clean up and flush logs.
    27502709     */
    2751 #ifdef LOG_ENABLED
    2752     RTLogSetCustomPrefixCallback(NULL, NULL, NULL);
    2753 #endif
    27542710    RTLogFlush(NULL);
    27552711}
  • trunk/src/VBox/VMM/VMMR3/VMM.cpp

    r72617 r72778  
    157157#define VMM_SAVED_STATE_VERSION_3_0 3
    158158
     159/** Macro for flushing the ring-0 logging. */
     160#define VMM_FLUSH_R0_LOG(a_pR0Logger, a_pR3Logger) \
     161    do { \
     162        PVMMR0LOGGER pVmmLogger = (a_pR0Logger); \
     163        if (!pVmmLogger || pVmmLogger->Logger.offScratch == 0) \
     164        { /* likely? */ } \
     165        else \
     166            RTLogFlushR0(a_pR3Logger, &pVmmLogger->Logger); \
     167    } while (0)
     168
    159169
    160170/*********************************************************************************************************************************
     
    417427#endif /* LOG_ENABLED */
    418428
     429    /*
     430     * Release logging.
     431     */
     432    PRTLOGGER pRelLogger = RTLogRelGetDefaultInstance();
     433    if (pRelLogger)
     434    {
    419435#ifdef VBOX_WITH_RC_RELEASE_LOGGING
    420     /*
    421      * Allocate RC release logger instances (finalized in the relocator).
    422      */
    423     if (VM_IS_RAW_MODE_ENABLED(pVM))
    424     {
    425         PRTLOGGER pRelLogger = RTLogRelGetDefaultInstance();
    426         if (pRelLogger)
     436        /*
     437         * Allocate RC release logger instances (finalized in the relocator).
     438         */
     439        if (VM_IS_RAW_MODE_ENABLED(pVM))
    427440        {
    428441            pVM->vmm.s.cbRCRelLogger = RT_OFFSETOF(RTLOGGERRC, afGroups[pRelLogger->cGroups]);
     
    432445            pVM->vmm.s.pRCRelLoggerRC = MMHyperR3ToRC(pVM, pVM->vmm.s.pRCRelLoggerR3);
    433446        }
    434     }
    435 #endif /* VBOX_WITH_RC_RELEASE_LOGGING */
     447#endif
     448
     449        /*
     450         * Ring-0 release logger.
     451         */
     452        RTR0PTR pfnLoggerWrapper = NIL_RTR0PTR;
     453        rc = PDMR3LdrGetSymbolR0(pVM, VMMR0_MAIN_MODULE_NAME, "vmmR0LoggerWrapper", &pfnLoggerWrapper);
     454        AssertReleaseMsgRCReturn(rc, ("vmmR0LoggerWrapper not found! rc=%Rra\n", rc), rc);
     455
     456        RTR0PTR pfnLoggerFlush = NIL_RTR0PTR;
     457        rc = PDMR3LdrGetSymbolR0(pVM, VMMR0_MAIN_MODULE_NAME, "vmmR0LoggerFlush", &pfnLoggerFlush);
     458        AssertReleaseMsgRCReturn(rc, ("vmmR0LoggerFlush not found! rc=%Rra\n", rc), rc);
     459
     460        size_t const cbLogger = RTLogCalcSizeForR0(pLogger->cGroups, 0);
     461
     462        for (VMCPUID i = 0; i < pVM->cCpus; i++)
     463        {
     464            PVMCPU pVCpu = &pVM->aCpus[i];
     465            rc = MMR3HyperAllocOnceNoRelEx(pVM, cbLogger, PAGE_SIZE, MM_TAG_VMM, MMHYPER_AONR_FLAGS_KERNEL_MAPPING,
     466                                           (void **)&pVCpu->vmm.s.pR0RelLoggerR3);
     467            if (RT_FAILURE(rc))
     468                return rc;
     469            PVMMR0LOGGER pVmmLogger = pVCpu->vmm.s.pR0RelLoggerR3;
     470            RTR0PTR      R0PtrVmmLogger = MMHyperR3ToR0(pVM, pVmmLogger);
     471            pVCpu->vmm.s.pR0RelLoggerR0     = R0PtrVmmLogger;
     472            pVmmLogger->pVM                 = pVM->pVMR0;
     473            pVmmLogger->cbLogger            = (uint32_t)cbLogger;
     474            pVmmLogger->fCreated            = false;
     475            pVmmLogger->fFlushingDisabled   = false;
     476            pVmmLogger->fRegistered         = false;
     477            pVmmLogger->idCpu               = i;
     478
     479            char szR0ThreadName[16];
     480            RTStrPrintf(szR0ThreadName, sizeof(szR0ThreadName), "EMT-%u-R0", i);
     481            rc = RTLogCreateForR0(&pVmmLogger->Logger, pVmmLogger->cbLogger, R0PtrVmmLogger + RT_OFFSETOF(VMMR0LOGGER, Logger),
     482                                  pfnLoggerWrapper, pfnLoggerFlush,
     483                                  RTLOGFLAGS_BUFFERED, RTLOGDEST_DUMMY, szR0ThreadName);
     484            AssertReleaseMsgRCReturn(rc, ("RTLogCreateForR0 failed! rc=%Rra\n", rc), rc);
     485
     486            /* We only update the release log instance here. */
     487            rc = RTLogCopyGroupsAndFlagsForR0(&pVmmLogger->Logger, R0PtrVmmLogger + RT_OFFSETOF(VMMR0LOGGER, Logger),
     488                                              pRelLogger, RTLOGFLAGS_BUFFERED, UINT32_MAX);
     489            AssertReleaseMsgRCReturn(rc, ("RTLogCopyGroupsAndFlagsForR0 failed! rc=%Rra\n", rc), rc);
     490
     491            pVmmLogger->fCreated = true;
     492        }
     493    }
     494
    436495    return VINF_SUCCESS;
    437496}
     
    581640         */
    582641#ifdef LOG_ENABLED
    583         if (    pVCpu->vmm.s.pR0LoggerR3
    584             &&  pVCpu->vmm.s.pR0LoggerR3->Logger.offScratch > 0)
    585             RTLogFlushR0(NULL, &pVCpu->vmm.s.pR0LoggerR3->Logger);
    586 #endif
     642        VMM_FLUSH_R0_LOG(pVCpu->vmm.s.pR0LoggerR3, NULL);
     643#endif
     644        VMM_FLUSH_R0_LOG(pVCpu->vmm.s.pR0RelLoggerR3, RTLogRelGetDefaultInstance());
    587645        if (rc != VINF_VMM_CALL_HOST)
    588646            break;
     
    833891         */
    834892#ifdef LOG_ENABLED
    835         if (    pVCpu->vmm.s.pR0LoggerR3
    836             &&  pVCpu->vmm.s.pR0LoggerR3->Logger.offScratch > 0)
    837             RTLogFlushR0(NULL, &pVCpu->vmm.s.pR0LoggerR3->Logger);
    838 #endif
     893        VMM_FLUSH_R0_LOG(pVCpu->vmm.s.pR0LoggerR3, NULL);
     894#endif
     895        VMM_FLUSH_R0_LOG(pVCpu->vmm.s.pR0RelLoggerR3, RTLogRelGetDefaultInstance());
    839896        if (rc != VINF_VMM_CALL_HOST)
    840897            break;
     
    10351092                AssertReleaseMsgRCReturn(rc, ("RTLogCreateForR0 failed! rc=%Rra\n", rc), rc);
    10361093
    1037                 RTR0PTR pfnLoggerPrefix = NIL_RTR0PTR;
    1038                 rc = PDMR3LdrGetSymbolR0(pVM, VMMR0_MAIN_MODULE_NAME, "vmmR0LoggerPrefix", &pfnLoggerPrefix);
    1039                 AssertReleaseMsgRCReturn(rc, ("vmmR0LoggerPrefix not found! rc=%Rra\n", rc), rc);
    1040                 rc = RTLogSetCustomPrefixCallbackForR0(&pR0LoggerR3->Logger,
    1041                                                        pVCpu->vmm.s.pR0LoggerR0 + RT_OFFSETOF(VMMR0LOGGER, Logger),
    1042                                                        pfnLoggerPrefix, NIL_RTR0PTR);
    1043                 AssertReleaseMsgRCReturn(rc, ("RTLogSetCustomPrefixCallback failed! rc=%Rra\n", rc), rc);
    1044 
    10451094                pR0LoggerR3->idCpu = i;
    10461095                pR0LoggerR3->fCreated = true;
    10471096                pR0LoggerR3->fFlushingDisabled = false;
    1048 
    10491097            }
    10501098
     
    14471495#endif
    14481496
     1497        /*
     1498         * Flush the logs
     1499         */
    14491500#ifdef LOG_ENABLED
    1450         /*
    1451          * Flush the log
    1452          */
    1453         PVMMR0LOGGER pR0LoggerR3 = pVCpu->vmm.s.pR0LoggerR3;
    1454         if (    pR0LoggerR3
    1455             &&  pR0LoggerR3->Logger.offScratch > 0)
    1456             RTLogFlushR0(NULL, &pR0LoggerR3->Logger);
    1457 #endif /* !LOG_ENABLED */
     1501        VMM_FLUSH_R0_LOG(pVCpu->vmm.s.pR0LoggerR3, NULL);
     1502#endif
     1503        VMM_FLUSH_R0_LOG(pVCpu->vmm.s.pR0RelLoggerR3, RTLogRelGetDefaultInstance());
    14581504        if (rc != VINF_VMM_CALL_HOST)
    14591505        {
     
    14931539        } while (rcStrict == VINF_EM_RAW_INTERRUPT_HYPER);
    14941540
     1541        /*
     1542         * Flush the logs
     1543         */
    14951544#ifdef LOG_ENABLED
    1496         /*
    1497          * Flush the log
    1498          */
    1499         PVMMR0LOGGER pR0LoggerR3 = pVCpu->vmm.s.pR0LoggerR3;
    1500         if (    pR0LoggerR3
    1501             &&  pR0LoggerR3->Logger.offScratch > 0)
    1502             RTLogFlushR0(NULL, &pR0LoggerR3->Logger);
    1503 #endif /* !LOG_ENABLED */
     1545        VMM_FLUSH_R0_LOG(pVCpu->vmm.s.pR0LoggerR3, NULL);
     1546#endif
     1547        VMM_FLUSH_R0_LOG(pVCpu->vmm.s.pR0RelLoggerR3, RTLogRelGetDefaultInstance());
    15041548        if (rcStrict != VINF_VMM_CALL_HOST)
    15051549            return rcStrict;
     
    25902634         */
    25912635#ifdef LOG_ENABLED
    2592         if (    pVCpu->vmm.s.pR0LoggerR3
    2593             &&  pVCpu->vmm.s.pR0LoggerR3->Logger.offScratch > 0)
    2594             RTLogFlushR0(NULL, &pVCpu->vmm.s.pR0LoggerR3->Logger);
    2595 #endif
     2636        VMM_FLUSH_R0_LOG(pVCpu->vmm.s.pR0LoggerR3, NULL);
     2637#endif
     2638        VMM_FLUSH_R0_LOG(pVCpu->vmm.s.pR0RelLoggerR3, RTLogRelGetDefaultInstance());
    25962639        if (rc != VINF_VMM_CALL_HOST)
    25972640            break;
  • trunk/src/VBox/VMM/include/VMMInternal.h

    r72617 r72778  
    452452    R0PTRTYPE(PVMMR0LOGGER)     pR0LoggerR0;
    453453
     454    /** Pointer to the R0 release logger instance - R3 Ptr.
     455     * This is NULL if logging is disabled. */
     456    R3PTRTYPE(PVMMR0LOGGER)     pR0RelLoggerR3;
     457    /** Pointer to the R0 release instance - R0 Ptr.
     458     * This is NULL if logging is disabled. */
     459    R0PTRTYPE(PVMMR0LOGGER)     pR0RelLoggerR0;
     460
    454461    /** Thread context switching hook (ring-0). */
    455462    RTTHREADCTXHOOK             hCtxHook;
  • trunk/src/VBox/VMM/include/VMMInternal.mac

    r69221 r72778  
    107107        .pR0LoggerR3            RTR3PTR_RES 1
    108108        .pR0LoggerR0            RTR0PTR_RES 1
     109        .pR0RelLoggerR3         RTR3PTR_RES 1
     110        .pR0RelLoggerR0         RTR0PTR_RES 1
    109111
    110112        .hCtxHook               RTR0PTR_RES 1
Note: See TracChangeset for help on using the changeset viewer.

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