
import random |
import time |
import EnvSetup.EnvSetup |
import Library.UFS.TestInstance as TestInstance |
# import config.UFS |
import os |
import copy |
import math |
import csv |
from Library.UFS.ufsDefines import * |
from Library.Generic import timer |
# Extra Imports |
from collections import namedtuple |
import collections |
class Latency_00_Command_Base(TestInstance.TestInstance): |
SIZE_1KB = 1024 |
SIZE_1MB = 1024 * SIZE_1KB |
SIZE_1GB = 1024 * SIZE_1MB |
LBA_SIZE = 4 * SIZE_1KB |
lbaStepSize = SIZE_1GB / LBA_SIZE |
DEBUG = False |
vcc = -1 |
vccq = -1 |
vccq2 = -1 |
atariCategory = 'Latency' |
atariGroup = 'TSM' |
outputUnit = 'ms' |
pwrMode = 'Active' |
exitOnFailure = False |
verbose = False |
type = 'Scsi' |
def AddCommandArgs(self): |
super(Latency_00_Command_Base, self).AddCommandArgs() |
# configure divice |
self.parser.add_argument('--mediaLunCount', type=int, default=1, help="""configure device with defalut(0ne) lun""") |
self.parser.add_argument('--bootLunCount', type=int, default=0, help="""configure device with default(default 0) boot Lun""") |
self.parser.add_argument('--competitor', type=bool, default=False, help="""whether the device comes from competitor. defalut False""") |
self.parser.add_argument('--tgtProvisionType', type=int, default=3, help="""provisioning type. discard,2,TPRZ=0/erase,3,TPRZ=1""") |
# argument for precondition |
self.parser.add_argument('--seqTransferTL', type=int, default=0x80, help="""sequential write TL, default 512K""") |
self.parser.add_argument('--seqData', type=int, default=0xAA, help="""sequential Write Data""") |
self.parser.add_argument('--randNumber', type=int, default=100000, help="""random write cmd count per GB""") |
self.parser.add_argument('--randData', type=int, default=0x55, help="""random write data""") |
# argument for data collection and flow contorl |
self.parser.add_argument('--basicCmdNum', type=int, default=100000, help="""command count for each iteration/GB""") |
self.parser.add_argument('--totalCmdNum', type=int, default=100000, help="""Sample points needed for data collection""") |
# argument for debug function |
# self.parser.add_argument('--specialSaveRawData', type=bool, default=False, help="""For special format save raw data collection""") |
self.parser.add_argument('--vuLatencyEn', type=bool, default=False, help="""If using latency VU, should use latency FW. print data profiling debug.""") |
self.parser.add_argument('--dumpCache', type=bool, default=False, help="""If necessary, open this. each cache count, script will print command info and vu data""") |
self.parser.add_argument('--vuCMDTO', type=float, default=30.0, help="""For read/sync/other command time out parameter. if command > 30ms, timeout""") |
self.parser.add_argument('--vuWriteTO', type=float, default=30.0, help="""Just for write, if command > 30ms, timeout""") |
self.parser.add_argument('--cacheCount', type=int, default=8, help="""save how many command into cache.""") |
# Others |
self.parser.add_argument('--defrageTable', type=bool, default=False, help="""dirty card or not""") |
def _reconfigDevice(self, bSecureRemovalTypeSet=0, bProvisionTypeReset=3): |
if not self.competitor: |
self.vuLib.vuCmdtranslate.restoreWriteOnce() |
response = self.lun[0].Query.ReadDescriptorSync(0x1, 0, 0, 0x90) |
reconfigBuffer = response.DataSegmentBuffer |
if reconfigBuffer.GetByte(2) != 0: |
reconfigBuffer.SetByte(2, 0) |
reconfigBuffer.SetByte(7, bSecureRemovalTypeSet) |
self.lun[0].Query.WriteDescriptorSync(0x1, 0, 0, 0x90, reconfigBuffer) |
maxAllocUinit = self.lib.query.getdEnhanced1MaxNAllocU() |
HighByte = (int(maxAllocUinit) & 0xFF00) >> 8 |
LowByte = int(maxAllocUinit) & 0xFF |
response = self.lun[0].Query.ReadDescriptorSync(1, 0, 0, 0x90) |
ReconfigBuf = response.DataSegmentBuffer |
for i in range(1, 9): |
ReconfigBuf.SetByte(i * 16 + 0xA, bProvisionTypeReset) |
ReconfigBuf.SetByte(i * 16, 0) |
ReconfigBuf.SetByte(i * 16 + 1, 0) |
ReconfigBuf.SetByte(i * 16 + 6, 0) |
ReconfigBuf.SetByte(i * 16 + 7, 0) |
ReconfigBuf.SetByte(0x16, HighByte) |
ReconfigBuf.SetByte(0x17, LowByte) |
ReconfigBuf.SetByte(0x10, 1) |
ReconfigBuf.SetByte(0x2, 0) |
self.lun[0].Query.WriteDescriptorSync(1, 0, 0, 0x90, ReconfigBuf) |
def _doBKOPs(self, defautTime=400): |
buf = self.mist.Buffer(0x1, LBA_SIZE) |
t = timer(defautTime) |
while not t.expired: |
if self.lib.query.getbBackgroundOpStatus() > 0: |
wExceptionEventStatus = self.lib.query.getwExceptionEventStatus() |
self.logger.info("send one read command") |
self.lun[self.lunID].Read10SimpleSync(0x1, 0x1, buf) |
self.logger.info("--------Get wExceptionEventStatus:{}".format(wExceptionEventStatus & 0x4)) |
time.sleep(60) |
else: |
break |
currentBKOPs = self.lib.query.getbBackgroundOpStatus() |
if currentBKOPs == 0 or currentBKOPs == 1: |
self.logger.info("current BKOPs has been completed") |
else: |
self.testFailed('bBackgroundOpStatus is still {} after {}'.format(currentBKOPs, defautTime)) |
def _newTargetConfg(self, bootOrNot=False): |
if not self.competitor: |
self.logger.info("########## Create new target configuration descriptor ##########") |
if bootOrNot: |
bootCapacity = 16 |
self.lib.lunConfig.createTargetConfig(mediaLunCount=self.mediaLunCount, bootLunCount=self.bootLunCount, setBigLun=True, |
useAllCapacity=True, setHighPriorityLun=False, |
bProvisioningType=self.tgtProvisionType, bootCapacity_MB=bootCapacity) |
else: |
self.lib.lunConfig.createTargetConfig(mediaLunCount=self.mediaLunCount, bootLunCount=self.bootLunCount, setBigLun=True, |
useAllCapacity=True, setHighPriorityLun=False, bProvisioningType=self.tgtProvisionType) |
self.lib.lunConfig.printDescriptor() # Print the new created target descriptor |
disabledLun = self.lib.lunConfig.getDisableLunInTargetConfig() |
self.logger.info("Disabled LUs are: %s" % disabledLun) |
targetCfgOutline = self.lib.lunConfig.getTargetConfigOutline() |
for key, value in targetCfgOutline.items(): |
self.logger.info("Category {} : {}".format(key, value)) |
# Write the new target configuration descriptor to device |
self.lib.lunConfig.writeTargetConfigToDevice() |
else: |
self.logger.info("the unit comes from competitor") |
def _latencyOfPurge(self, purgeTimeOut=None): |
""" |
Original purgeTimeoOut=4000 |
Purge all unmaped LBA(s) |
NOTE: This latency is in increments of 1ms |
:param purgeTimeOut: (int) in seconds the timeout period to wait for the purge command |
:return usPurgeTime: (float) command execution time in microseconds (us) |
""" |
self.logger.info("***********************do purge*********************") |
if purgeTimeOut is None: |
purgeTimeOut = self.cfg.timeout.purgeTimeout |
# Just for leo, to pass this case. |
if self.product == "Leo": |
purgeTimeOut = 10000 |
setPurgeResp = self.lib.query.setfPurgeEnable() |
if setPurgeResp == 1: |
purgeStartTime = time.time() |
else: |
self.testFailed( |
"Set fPurgeEnable flag enable fail, and the fPurgeEnable flag value is {}".format(setPurgeResp)) |
bPurgeStatus = -1 |
while bPurgeStatus not in (PURGE_STATUS_STOPPED_BY_HOST, PURGE_STATUS_COMPLETED): |
bPurgeStatus = self.lib.query.getbPurgeStatus() |
if time.time() - purgeStartTime > purgeTimeOut: |
self.testFailed("FAIL bPurgeStatus ({}) not set to {} before timeOut {}".format(setPurgeResp, ( |
PURGE_STATUS_STOPPED_BY_HOST, PURGE_STATUS_COMPLETED), purgeTimeOut)) |
purgeEndTime = time.time() |
secondsPurgeTime = purgeEndTime - purgeStartTime |
usPurgeTime = self.lib.generic.changeUnits("us", "seconds", secondsPurgeTime) |
return usPurgeTime |
def _eraseFullCardPost(self): |
self.logger.info("pre/post condition: format unit and purge") |
lunList = self.lib.reportLuns.getAvailableNormalLuns() |
for lunID in lunList: |
self.lun[lunID].FormatUnitSimpleSync() |
self._latencyOfPurge() |
def _defragTableVB(self): |
tl = 1 |
buf = self.mist.Buffer(1, 8) |
maxLba = self.lun[self.lunID].ReadCapacity10Sync(buf).MaxLba |
writeBuffer = self.mist.Buffer(tl, LBA_SIZE) |
writeBuffer.Fill(0x55) |
for i in range(12): |
startLba = random.randint(0, 1020) |
while startLba < maxLba: |
if startLba + tl > maxLba: |
startLba = maxLba - tl |
self.lun[self.lunID].Write10Sync(lba=startLba, count=tl, disablePageOut=False, fua=False, fuaNv=False, buf=writeBuffer) |
startLba = startLba + 1024 |
def _sendWriteCMD(self, startLba, endLba, transferLen=1, writeData=0xAA, cmdNumber=0, seq=False, QD=32): |
outstandingCmd = {} |
responseList = [] |
timeout = 60 |
buffers = [self.mist.Buffer(transferLen, LBA_SIZE) for _ in range(QD)] |
buffTrk = range(QD) |
temp = 0 |
if seq: |
self.logger.debug("sequential write from startLba=--{}-- to endLba=--{}--".format(startLba, endLba)) |
while (startLba <= endLba): |
if startLba + transferLen > endLba: |
tempTL = endLba - startLba + 1 |
else: |
tempTL = transferLen |
if len(outstandingCmd) < QD: |
buffNum = buffTrk.pop() |
buf = buffers[buffNum] |
buf.Fill(writeData) |
usr = self.lun[self.lunID].Write10Simple(startLba, tempTL, buf) |
outstandingCmd[usr.SequenceId] = (buffNum) |
if len(outstandingCmd) == QD: |
startTime = time.time() |
while len(responseList) == 0: |
elapsedTime = time.time() - startTime |
if elapsedTime > timeout: |
break |
try: |
responseList = self.lun[self.lunID].CompletedResponses |
except self.mist.ufs.UpiuCommandError as e: |
self.testFailed("UpiuCommandError happened {}".format(e)) |
except Exception as e: |
self.testFailed("Exception happened {}".format(e)) |
for resp in responseList: |
sequenceId = resp.SequenceId |
buffNum = outstandingCmd.pop(sequenceId) |
buffTrk.append(buffNum) |
responseList = [] |
startLba += tempTL |
if startLba == endLba + 1: |
if 0 < len(outstandingCmd) < QD: |
for i in range(len(outstandingCmd)): |
self.lun[self.lunID].WaitForOneResponse() |
else: |
if cmdNumber: |
self.logger.debug("random send cmdNumber={} write command in startLba={},endLba={}".format(cmdNumber, startLba, endLba)) |
while (temp < cmdNumber): |
if len(outstandingCmd) < QD: |
buffNum = buffTrk.pop() |
buf = buffers[buffNum] |
buf.Fill(writeData) |
tempLba = random.randint(startLba, endLba - transferLen + 1) |
usr = self.lun[self.lunID].Write10Simple(tempLba, transferLen, buf) |
temp = temp + 1 |
outstandingCmd[usr.SequenceId] = (buffNum) |
if len(outstandingCmd) == QD: |
startTime = time.time() |
while len(responseList) == 0: |
elapsedTime = time.time() - startTime |
if (elapsedTime > timeout) and len(responseList) == 0: |
self.testFailed("Timeout occurred after %s,no responses returned " % elapsedTime) |
break |
try: |
responseList = self.lun[self.lunID].CompletedResponses |
except self.mist.ufs.UpiuCommandError as e: |
self.testFailed("UpiuCommandError happened {}".format(e)) |
except Exception as e: |
self.testFailed("Exception happened {}".format(e)) |
for resp in responseList: |
sequenceId = resp.SequenceId |
buffNum = outstandingCmd.pop(sequenceId) |
buffTrk.append(buffNum) |
responseList = [] |
if temp == cmdNumber: |
for i in range(len(outstandingCmd)): |
self.lun[self.lunID].WaitForOneResponse() |
else: |
writeBuffer = self.mist.Buffer(transferLen, LBA_SIZE) |
writeBuffer.Fill(writeData) |
self.logger.debug("random send write command on current lbaRange until all LBA has been written") |
self.lib.writeReadAsync.writeRandom(self.lunID, "Write10", writeBuffer, startLba, endLba, transferLen, QD, fua=True) |
def _overwriteCli(self): |
self.responseLoggerIsEnabled = False |
self.responseLoggerReportUpiu = False |
self.responseLoggerReportFailOnly = True |
self.needstandardTestStart = False |
def _preConditionForSAS(self, SSPreCondition=False): |
if self.DEBUG: |
self.logger.info('DEBUG running, precondition skipped') |
return |
self.logger.info("sequential write full card with ChunkSize *** 512KB ***, data is 0xAA") |
buff = self.mist.Buffer(1, 8) |
endLba = self.lun[self.lunID].ReadCapacity10Sync(buff).MaxLba |
self._sendWriteCMD(0, endLba, transferLen=self.seqTransferTL, seq=True) |
if not SSPreCondition: |
self.logger.info("============Random write 100000 per GB with pattern 0x55============") |
for startLba, endLba in self.LbaRange: |
self._sendWriteCMD(startLba, endLba, writeData=self.randData, cmdNumber=self.randNumber) |
else: |
self.logger.info("precondition for SS's test method") |
cmdNumber = self.maxLba / self.seqTransferTL |
self._sendWriteCMD(0, self.maxLba, transferLen=self.seqTransferTL, writeData=self.randData, cmdNumber=cmdNumber) |
if self.defrageTable: |
self._defragTableVB() |
def _preCondition(self, lunID, loopSize=1000): |
""" |
Precondition options you can overwrite by children classes. |
:param lunID: current lun |
:return oldValues: (dict) the values before this method changed them |
:return avgTeUnRe: (float) average test unit ready time |
:return secIdleBoundary: (float) time to put device in idle mode in seconds |
""" |
newValues = {} |
if hasattr(self, 'newWce') and self.newWce >= 0: |
newValues["newWce"] = self.newWce |
if hasattr(self, 'newRcd') and self.newRcd >= 0: |
newValues["newRcd"] = self.newRcd |
if hasattr(self, 'gearHS') and self.gearHS >= 0: |
newValues["gearHS"] = self.gearHS |
if hasattr(self, 'rateHS') and self.rateHS != "None": |
newValues["rateHS"] = self.rateHS |
if hasattr(self, 'ufsPwrMode') and self.powerMode >= 0: |
newValues["ufsPwrMode"] = self.ufsPwrMode |
if hasattr(self, 'laneNum') and self.laneNum >= 0: |
newValues["laneNum"] = self.laneNum |
if hasattr(self, 'bDataReliability') and self.bDataReliability >= 0: |
newValues["bDataReliability"] = self.bDataReliability |
newValues["newVoltages"] = self.lib.performanceAdHoc.getVoltages( |
vcc=self.vcc, vccq=self.vccq, vccq2=self.vccq2) |
oldValues = self.lib.performanceAdHoc.latencyPreCondition(lunID=lunID, newValues=dict(newValues), verbose=False, |
exitOnFailure=self.exitOnFailure) |
self.logger.parameter(paramName="newValues", value=newValues, category=self.atariCategory, group=self.atariGroup) |
self.logger.parameter(paramName="oldValues", value=oldValues, category=self.atariCategory, group=self.atariGroup) |
(usAvgTeUnRe, _, _) = self.lib.performanceAdHoc.testUnitReadyLoopAndPrint(lunID=lunID, loopSize=loopSize, verbose=False) |
avgTeUnRe = self.lib.generic.changeUnits(self.outputUnit, "us", usAvgTeUnRe) |
self.logger.parameter(paramName="Test Unit Ready Average", value=avgTeUnRe, unit=self.outputUnit, category=self.atariCategory, group=self.atariGroup) |
if self.pwrMode == "Idle": |
(_, rawBoundary, rawUnit) = self.cfg.latency.getLatencyExpected("Typical", "Active to Idle") |
idleBoundary = self.lib.generic.changeUnits(self.outputUnit, rawUnit, rawBoundary) |
else: |
idleBoundary = None |
preCondition = namedtuple("preCondition", "oldValues avgTeUnRe secIdleBoundary") |
return preCondition(oldValues=oldValues, avgTeUnRe=avgTeUnRe, secIdleBoundary=idleBoundary) |
def _dumpCacheData(self): |
self.logger.info("===================Dump CMD/VU Buffer=======================") |
self.logger.info("NOTE: CMD Info Format: (cmdType, lun, lba, transferLen, taskTag, sequenceId, exec(ms)") |
self.logger.info("Debug. cache cmd count:{}".format(len(self.vuCacheDict))) |
for cmdKey, vuValue in self.vuCacheDict.items(): |
tempList = [] |
tempList.append(cmdKey) |
self.logger.info("cmdKey(ms):{}".format(cmdKey)) |
self.logger.info("vuValue:{}".format(vuValue)) |
for index, tempValue in enumerate(vuValue): |
tempList.append(tempValue) |
self._saveRawData(["TimeOut_VU_Get_Latency"], [tempList]) |
# if the whole cache data are dumped, clear cache. |
self.vuCacheDict.clear() |
def _vuLatency(self, cmdInfo, checkTO=True): |
# cmdInfo = (cmdType, lun, lba, transferLen, taskTag, sequenceId, execTime) |
cmdType = cmdInfo[0] |
execTime = cmdInfo[-1] |
if self.cacheCount: |
if len(self.vuCacheDict) >= self.cacheCount: |
if self.dumpCache: |
self._dumpCacheData() |
else: |
# if no dump data, then pop one item for the next cmd info. |
self.vuCacheDict.popitem(0) |
vuData = self.vuLib.vuCmdtranslate.getProfileDataLatency() |
self.vuCacheDict[cmdInfo] = vuData |
# Check command whether timeout.if yes, test failed immediately. |
if cmdType == 'Mode Sense': |
self.vuLatencyTO = 1 |
if cmdType == "Mode Select": |
self.vuLatencyTO = 100 |
self.logger.info("111----set command timeout {}ms for cmdType {}".format(self.vuLatencyTO, cmdType)) |
if checkTO and execTime >= self.vuLatencyTO: |
if self.cacheCount: |
self._dumpCacheData() |
try: |
vuData = self.vuLib.vuCmdtranslate.getProfileDataLatency() |
self.logger.info("Oops! Occurred cmd time out!, TargetTO:{} ms".format(self.vuLatencyTO)) |
self.logger.info("NOTE: CMD Info Format: (cmdType, lun, lba, transferLen, taskTag, sequenceId, exec(ms)") |
self.logger.info("CMD Info(ms):{}, ".format(cmdInfo)) |
self.testFailed("GetProfileData:{}".format(vuData)) |
except self.mist.ufs.UpiuCommandError as e: |
self.logger.info("Get profileData error:{}".format(e)) |
def _postCondition(self, lunID, oldValues): |
""" |
Postcondition options you can overwrite by children classes. |
:param lunID: current lun |
:param oldValues: (dict) the values to set the device back to before ending the next test |
""" |
self.lib.performanceAdHoc.latencyPostCondition(lunID=lunID, oldValues=oldValues) |
def _afterLoopBeforeAveraging(self, usLatencies): |
""" |
Some scripts need to modify the latencies before proceeding with the test |
:param lunID: (int) current lun |
:param usLatencies: [int] The latencies from the main function in micro seconds |
:returns latencies: [int] The modified latencies |
""" |
latencies = self.lib.generic.changeUnits(self.outputUnit, "us", usLatencies) |
return latencies |
def _splitdrive(self, p): |
"""Split a pathname into drive and path specifiers. Returns a 2-tuple |
"(drive,path)"; either part may be empty""" |
if p[1:2] == ':': |
return p[0:2], p[2:] |
return '', p |
def split(self, p): |
"""Split a pathname. |
Return tuple (head, tail) where tail is everything after the final slash. |
Either part may be empty.""" |
d, p = self._splitdrive(p) |
# set i to index beyond p's last slash |
i = len(p) |
while i and p[i - 1] not in '/\\': |
i = i - 1 |
head, tail = p[:i], p[i:] # now tail has no slashes |
# remove trailing slashes from head, unless it's all slashes |
head2 = head |
while head2 and head2[-1] in '/\\': |
head2 = head2[:-1] |
head = head2 or head |
return d + head, tail |
def _openNewFile(self, fName): |
# arg maybe include new csv file |
if fName: |
tempCsv = fName + "_Latencies_data.csv" |
else: |
tempCsv = "Command_Latencies_data.csv" |
if self.logFilePath.endswith('.log'): |
subPath2, _ = self.split(self.logFilePath) |
subPath1, _ = self.split(subPath2) |
filePath, _ = self.split(subPath1) |
else: |
filePath = self.logFilePath |
fileName = os.path.join(filePath, tempCsv) |
if os.path.exists(fileName): |
self.logger.info("-------file exist--------") |
return fileName |
def _calSTD(self, sortedRawList=None, avgTime=0): |
n = len(sortedRawList) |
if n == 1: |
return 0 |
tempList = [pow((item - avgTime), 2) for item in sortedRawList] |
stdDeviation = math.sqrt(sum(tempList) / float(n - 1)) |
return stdDeviation |
def classOfNines(self, data, outliers=100): |
""" |
For quality of service (QoS) we usually need to print not only the maximum but also the 99% largest element |
:param data: All data points collected, probably latencies |
:type data: list |
:param outliers: Have at least this many outliers before calculating that class of nines |
:type outliers: int |
:return results: The class of nines results |
:rtype results: dict |
Example |
results = classOfNines(range(1, 10000000+1)) |
print [(key, results[key]) for key in sorted(results.keys())] |
""" |
data.sort() |
results = {} |
nines = [0.99, 0.999, 0.9999, 0.99999, 0.999999, 0.9999999, 0.99999999, 0.999999999, 0.9999999999] |
n = 0 |
listLen = len(data) |
while True: |
if not listLen / 10 ** (n + 2) >= outliers: |
break |
index = int(nines[n] * listLen) - 1 |
results[nines[n]] = data[index] |
n += 1 |
return results |
def _calAllData(self, cmdType, cmdName, rawData, sampleNumber=None): |
""" |
Statistics of raw latency data |
:param cmdType: Category of the operation, eg, QoS, SCSI, UPIU |
:param cmdName: Details of the cmd, eg, 4K read dirty |
:param rawData: raw latency data |
:param sampleNumber: the count of data points collected |
:return: statistics data list, eg, [['QoS', '4K read dirty', 'Min', 20], ['QoS', '4K read dirty', 'Max', 30]...] |
""" |
rawDataList = copy.deepcopy(rawData) |
if not sampleNumber: |
sampleNumber = len(rawData) |
rawDataList.sort() |
if len(rawDataList) == 1: |
avgTime = rawDataList[0] |
maxTime = rawDataList[0] |
minTime = rawDataList[0] |
median = rawDataList[0] |
stdDeviation = 0 # standard deviation |
else: |
avgTime = float(sum(rawDataList)) / len(rawDataList) |
maxTime = max(rawDataList) |
minTime = min(rawDataList) |
sortedTimes = sorted(rawDataList) |
if (len(sortedTimes) == 0): |
median = 0 |
else: |
if (len(sortedTimes) % 2) == 1: |
median = sortedTimes[len(sortedTimes) / 2] |
else: |
median = (sortedTimes[(len(sortedTimes) / 2) - 1] + sortedTimes[(len(sortedTimes) / 2)]) / 2 |
stdDeviation = self._calSTD(rawDataList, avgTime) |
keys = [] |
values = [] |
countNum = 0 |
nines = self.classOfNines(rawDataList) |
nines1 = sorted(nines.iteritems(), key=lambda d: d[1], reverse=False) |
if len(rawDataList) < 10000: |
values = ["NA", "NA", "NA"] |
else: |
for key, value in nines1: |
keys.append(key) |
values.append(value) |
countNum = countNum + 1 |
if countNum == 3: |
break |
if len(keys) == 1: |
keys.extend(["NA","NA"]) |
values.extend(["NA","NA"]) |
if len(keys) == 2: |
keys.append("NA") |
values.append("NA") |
allData = [ |
[cmdType] + [cmdName] + ['Min'] + [minTime], |
[cmdType] + [cmdName] + ['Avg'] + [avgTime], |
[cmdType] + [cmdName] + ['Median'] + [median], |
[cmdType] + [cmdName] + ['Std Dev'] + [stdDeviation], |
[cmdType] + [cmdName] + ['99%'] + [values[0]], |
[cmdType] + [cmdName] + ['99.9%'] + [values[1]], |
[cmdType] + [cmdName] + ['99.99%'] + [values[2]], |
[cmdType] + [cmdName] + ['Max'] + [maxTime], |
[cmdType] + [cmdName] + ['sampleNumber'] + [sampleNumber] |
] |
# allData = [cmdType] + [CMD] + [minTime, avgTime, median, stdDeviation] + values + [maxTime, sampleNumber] |
self.logger.info("-----------------------------------------------------------------------------------------------------------") |
self.logger.info("{:<16} {:<48} {:<8} {:<16} {:<8} {:<16} {:<8} {:<8} {:<8} {:<8} {:<8}". |
format("cmdType", "CMD", "MinTime", "AVERAGE", "Median", "STD", "99%", "99.9%", "99.99%", |
"MaxTime", "sampleNumber")) |
self.logger.info("{:<16} {:<48} {:<8} {:<16} {:<8} {:<16} {:<8} {:<8} {:<8} {:<8} {:<8}". |
format(cmdType, cmdName, minTime, avgTime, median, stdDeviation, values[0], |
values[1], values[2], maxTime, sampleNumber)) |
self.logger.info("------------------------------------------------------------------------------------------------------------") |
return allData |
def _handleLatencies(self, allData, fName=None): |
# allData---type: list or int(no raw data) |
# file name---type str without(.csv):new csv file default: |
fileName = self._openNewFile(fName) |
wrFileHdlr = open(fileName, "ab+") |
wt = csv.writer(wrFileHdlr) |
for i in xrange(len(allData)): |
data = allData[i] |
wt.writerow(data) |
wrFileHdlr.close() |
def _saveRawData(self, nameList, rawDataList): |
""" |
Save raw latency data to csv file |
:param nameList: a list which contains the cm names, used in csv file naming |
:param rawDataList: raw data corresponding to nameList |
:return: None |
""" |
for i in range(len(nameList)): |
if self.logFilePath.endswith('.log'): |
rawDataFile = self.logFilePath.rstrip('.log') + "_" + nameList[i] + "_raw_data.csv" |
else: |
rawDataFile = self.logFilePath + "/" + self.logFileName[:-4] + "_" + nameList[i] + "_raw_data.csv" |
wrFileHdlr = open(rawDataFile, "a+") |
for execTime in rawDataList[i]: |
wrFileHdlr.write("{}\n".format(execTime)) |
wrFileHdlr.close() |
def _rawDataFileName(self, name): |
""" |
Get target raw data file, related to func _saveRawData() |
:param name: cmd name |
:return: absolute raw data file path |
""" |
if self.logFilePath.endswith('.log'): |
rawDataFile = self.logFilePath.rstrip('.log') + "_" + name + "_raw_data.csv" |
else: |
rawDataFile = self.logFilePath + "/" + self.logFileName[:-4] + "_" + name + "_raw_data.csv" |
return rawDataFile |
def _nandTemperaturePrint(self): |
"""Print the current NAND temperature in Celsius |
""" |
temp = self.lib.vuHighLevelLib.getNandTemperature() |
self.logger.info(temp) |
def _generateCommandIndex(self, startLba=0, endLba=0, chunkSize=1, randCount=100000, percentage=50): |
# random address list without overlap |
def randrange(start, end, size, randCount): |
if ((end - start) < (size - 1)) or (len(lbaList) == randCount): |
return 0 |
lba = random.randint(start, end - size + 1) |
lbaList.append(lba) |
randrange(start, lba - 1, size, randCount) |
randrange(lba + size, end, size, randCount) |
# Make sure keep more than 50% UDA after unmap. if necessary, should rerun the test to collect 100,000 |
UDAPerct = (endLba - startLba + 1) * percentage / 100 |
gpNum = UDAPerct / chunkSize |
sampleCnt = gpNum if gpNum < randCount else randCount |
if chunkSize == 1: |
cmdIndex = [[tempLba, tempLba + chunkSize - 1] for tempLba in |
random.sample(xrange(startLba, endLba + 1 - chunkSize + 1), sampleCnt)] |
else: |
lbaList = [] |
randrange(startLba, endLba, chunkSize, randCount) |
cmdIndex = [[tempLba, tempLba + chunkSize - 1] for tempLba in random.sample(lbaList, sampleCnt)] |
return cmdIndex |
def _getUnmapTimeQD(self, unmapCMDIndex=None, unmapSize=0x1, queueDepth=8): |
responseList = [] |
unmapLatencies = [] |
outstandingCmd = {} |
outstandingCmdTotalCnt = 0 |
while (unmapCMDIndex): |
if (len(outstandingCmd) < queueDepth): |
currentCmd = unmapCMDIndex.pop(0) |
startLba = currentCmd[0] |
usr = self.lun[self.lunID].UnmapSimple(startLba, unmapSize) |
outstandingCmdTotalCnt += 1 |
outstandingCmd[usr.SequenceId] = ('Unmap', 0, currentCmd[0], unmapSize) |
else: |
t = timer(30) |
while (not t.expired) and (len(responseList) == 0): |
try: |
responseList = self.lun[self.lunID].CompletedResponses |
except Exception as e: |
self.testFailed("Exception occurred: %s" % e) |
if t.expired: |
self.testFailed("Timeout occurred after %s,no responses returned " % t) |
for resp in responseList: |
execTime = float(resp.ExecTime * 10 ** -3) |
sequenceId = resp.SequenceId |
unmapLatencies.append(execTime) |
outstandingCmd.pop(sequenceId) |
# cmdType, buffNum, randLba, tranfLen = outstandingCmd.pop(sequenceId) |
# # For fw request, add vu to get command's ingo |
# if self.vuLatencyEn and not self.competitor: |
# cmdInfo = (cmdType, self.lunID, randLba, tranfLen, resp.TaskTag, sequenceId, execTime) |
# self._vuLatency(cmdInfo) |
responseList = [] |
while len(outstandingCmd): |
try: |
responseList = self.lun[self.lunID].WaitForAllResponses() |
except Exception as e: |
self.testFailed("Exception occurred: %s" % e) |
for resp in responseList: |
execTime = float(resp.ExecTime * 10 ** -3) |
sequenceId = resp.SequenceId |
unmapLatencies.append(execTime) |
cmdType, buffNum, randLba, tranfLen = outstandingCmd.pop(sequenceId) |
# For fw request, add vu to get command's info |
# if self.vuLatencyEn and not self.competitor: |
# cmdInfo = (cmdType, self.lunID, randLba, tranfLen, resp.TaskTag, sequenceId, execTime) |
# self._vuLatency(cmdInfo) |
# |
# # If dump the remaining cache data. |
# if self.vuLatencyEn and not self.competitor and self.dumpCache and len(self.vuCacheDict): |
# self.logger.info("The last vcCacheDice length:{}".format(len(self.vuCacheDict))) |
# self._dumpCacheData() |
return unmapLatencies |
def _collectUnmapLatencies(self, unmapSize=0x1, QD=8, totalCnt=100000, maxUnmapPert=100): |
unmapLatenciesList = [] |
unmapCMDIndex = self._generateCommandIndex(0, self.maxLba, chunkSize=unmapSize, randCount=totalCnt, |
percentage=maxUnmapPert) |
unmapLatenciesList.extend(self._getUnmapTimeQD(unmapCMDIndex, unmapSize, QD)) |
while (len(unmapLatenciesList) < totalCnt): |
self.logger.info('{} sampling points collected, switch to next iteration'.format(len(unmapLatenciesList))) |
self._preConditionForSAS() |
unmapCMDIndex = self._generateCommandIndex(0, self.maxLba, chunkSize=unmapSize, |
randCount=(totalCnt - len(unmapLatenciesList))) |
unmapLatenciesList.extend(self._getUnmapTimeQD(unmapCMDIndex, unmapSize, QD)) |
return unmapLatenciesList |
def _getVariables(self, lunID): |
""" |
Return the variable needed for this script to run. This method is overwritten by children classes. |
:param lunID: (int) Current LUN |
:returns cmdCalls ([functions]) The commands that the latencies are to be taken from |
:returns cmdNames ([str]) Names of the commands in cmdCalls. Set to None if you don't want latencies from that command. |
:returns isSequential (bool) When looping through should commands be run in order like 111122223333 (True) or 123123123123 (False) |
""" |
def buffer_(): |
return self.mist.Buffer(sectorCount=1, bytesPerSector=LBA_SIZE) |
def inquiry(): |
return self.lun[lunID].InquirySync(enableVitalProductData=True, pageCode=0, buf=buffer_()) |
cmdCalls = [inquiry] |
cmdNames = ["Inquiry"] |
isSequential = True |
getVariables = namedtuple("getVariables", "cmdCalls cmdNames isSequential") |
return getVariables(cmdCalls=cmdCalls, cmdNames=cmdNames, isSequential=isSequential) |
def _enterAndExitH8(self, delay=0): |
H8_Exit_lat = [] |
respHibern8Enter = self.lib.dme.hibernateEnter() |
if (respHibern8Enter != 0): |
self.addFailure("Hibern8 enter failed. status is {} ".format(respHibern8Enter)) |
self.logger.info('Step2.1-----Wait 5s, and monitor Iccq2---') |
time.sleep(delay * 1.0 / 1000) |
# self.lib.powerMeasurement.measureVccCurrent(1000, delay=1) |
if delay == 0: |
one_million_samples = 1 * 1000 * 1000 |
self.lib.powerMeasurement.measureVccqVccq2Current(samples=2 * one_million_samples, sampleRate=250, test="Idle", |
temperature="25C", hibernate=True) |
self.logger.info("Step2.2-----Exit hibernate -------") |
response = self.lun[self.lun.keys()[0]].Dme.HibernateExit() |
if ((response.Argument2 & 0xFF) != 0): |
self.addFailure("Hibern8 Exit DME command failed. error code is {} ".format(response.Argument2)) |
else: |
self.logger.info("exec time of exit hibernate is {}us".format(response.ExecTime)) |
H8_Exit_lat.append(response.ExecTime) |
def getBigLunId(self): |
lunList = self.lib.reportLuns.getAvailableNormalLuns() |
bigLun = 0 |
maxLbaBigLun = 0 |
for lunId in lunList: |
buf = self.mist.Buffer(1, 8) |
tempLba = self.lun[lunId].ReadCapacity10Sync(buf).MaxLba |
if tempLba > maxLbaBigLun: |
bigLun = lunId |
maxLbaBigLun = tempLba |
maxUnmapLbaSize = self.lib.unmapAsync._getMaxUnmapLbaSize(bigLun) |
intCapacityGB = maxLbaBigLun / self.lbaStepSize |
maxLba = intCapacityGB * self.lbaStepSize |
lbaMainStart = 0 |
LbaRange = [(startLba, endLba) for startLba, endLba in |
zip(range(lbaMainStart, maxLba - self.lbaStepSize, self.lbaStepSize), range(lbaMainStart + self.lbaStepSize - 1, maxLba, self.lbaStepSize))] |
return bigLun, maxLbaBigLun, LbaRange, maxUnmapLbaSize |
def getLatencies(self, cmdCalls, isSequential, secIdleBoundary): |
usLatencies = self.lib.performanceAdHoc.loopCmd( |
command=cmdCalls, loopSize=self.totalCmdNum, sequential=isSequential, idleSleep=secIdleBoundary, verbose=self.verbose) |
return usLatencies |
def Test(self): |
""" |
This is the main test function of the TC Script. |
""" |
self._eraseFullCardPost() |
self.logger.info("<Step1> PRECONDITION: Lun config") |
self._newTargetConfg() |
self.lunID, self.maxLba, self.LbaRange, self.maxUnmapLbaSize = self.getBigLunId() |
self.vuCacheDict = collections.OrderedDict() |
self.vuLatencyTO = self.vuCMDTO |
self.logger.info("Lun {}".format(self.lunID)) |
# ####### VARIABLES ####### # |
cmdCalls, cmdNames, isSequential = self._getVariables(self.lunID) |
self.cmdNames = cmdNames |
# ####### PRECONDITION ####### # |
self.logger.info("<Step2> Send 1000 * TUR to measure host overhead ") |
oldValues, avgTeUnRe, idleBoundary = self._preCondition(lunID=self.lunID) |
secIdleBoundary = self.lib.generic.changeUnits("seconds", self.outputUnit, idleBoundary) |
# ####### MAIN ####### # |
self.logger.info("<Step3> Send {} * {} commands to measure latency".format(self.totalCmdNum, cmdNames)) |
usLatencies = self.getLatencies(cmdCalls, isSequential, secIdleBoundary) |
latencies = self._afterLoopBeforeAveraging(usLatencies=usLatencies) |
self._saveRawData(self.cmdNames, latencies) |
# make sure to use len(latencies) in case of duplicated cmdNames |
for i in xrange(len(latencies)): |
allData = self._calAllData(self.type, cmdNames[i], latencies[i]) |
self._handleLatencies(allData, self.logFileName[:-4]) |
self.logger.debug("POSTCONDITION") |
self._postCondition(lunID=self.lunID, oldValues=oldValues) |
self._eraseFullCardPost() |
def Main(): |
test = Latency_00_Command_Base() |
exit(test.Run()) |
if __name__ == "__main__": |
Main() |




中级程序员
by: 小鬼哈哈哈 发表于:2020-02-25 16:34:30 顶(0) | 踩(0) 回复
这个是干嘛的?
回复评论