1 | package de.uka.ipd.sdq.dsexplore.analysis.lqn; |
2 | |
3 | import java.text.ParseException; |
4 | import java.util.ArrayList; |
5 | import java.util.Iterator; |
6 | import java.util.List; |
7 | import java.util.Map; |
8 | |
9 | import org.apache.log4j.Logger; |
10 | import org.eclipse.emf.common.util.EList; |
11 | import org.eclipse.emf.common.util.TreeIterator; |
12 | import org.eclipse.emf.ecore.EObject; |
13 | import org.eclipse.emf.ecore.util.EcoreUtil; |
14 | import org.opt4j.core.Criterion; |
15 | |
16 | import LqnCore.ActivityDefType; |
17 | import LqnCore.ActivityPhasesType; |
18 | import LqnCore.EntryType; |
19 | import LqnCore.LqnModelType; |
20 | import LqnCore.OutputResultType; |
21 | import LqnCore.ProcessorType; |
22 | import LqnCore.TaskType; |
23 | import de.uka.ipd.sdq.dsexplore.analysis.AbstractPerformanceAnalysisResult; |
24 | import de.uka.ipd.sdq.dsexplore.analysis.AnalysisFailedException; |
25 | import de.uka.ipd.sdq.dsexplore.helper.EMFHelper; |
26 | import de.uka.ipd.sdq.dsexplore.qml.pcm.datastructures.EntryLevelSystemCallCriterion; |
27 | import de.uka.ipd.sdq.dsexplore.qml.pcm.datastructures.EvaluationAspectWithContext; |
28 | import de.uka.ipd.sdq.dsexplore.qml.pcm.datastructures.UsageScenarioBasedCriterion; |
29 | import de.uka.ipd.sdq.pcm.allocation.AllocationContext; |
30 | import de.uka.ipd.sdq.pcm.core.composition.AssemblyContext; |
31 | import de.uka.ipd.sdq.pcm.repository.BasicComponent; |
32 | import de.uka.ipd.sdq.pcm.repository.OperationSignature; |
33 | import de.uka.ipd.sdq.pcm.repository.PassiveResource; |
34 | import de.uka.ipd.sdq.pcm.repository.Repository; |
35 | import de.uka.ipd.sdq.pcm.repository.RepositoryComponent; |
36 | import de.uka.ipd.sdq.pcm.resourceenvironment.LinkingResource; |
37 | import de.uka.ipd.sdq.pcm.resourceenvironment.ProcessingResourceSpecification; |
38 | import de.uka.ipd.sdq.pcm.resourceenvironment.ResourceContainer; |
39 | import de.uka.ipd.sdq.pcm.resultdecorator.ResultDecoratorRepository; |
40 | import de.uka.ipd.sdq.pcm.resultdecorator.ResultdecoratorFactory; |
41 | import de.uka.ipd.sdq.pcm.resultdecorator.repositorydecorator.AllocationServiceResult; |
42 | import de.uka.ipd.sdq.pcm.resultdecorator.repositorydecorator.RepositorydecoratorFactory; |
43 | import de.uka.ipd.sdq.pcm.resultdecorator.repositorydecorator.ServiceResult; |
44 | import de.uka.ipd.sdq.pcm.resultdecorator.resourceenvironmentdecorator.LinkingResourceResults; |
45 | import de.uka.ipd.sdq.pcm.resultdecorator.resourceenvironmentdecorator.PassiveResourceResult; |
46 | import de.uka.ipd.sdq.pcm.resultdecorator.resourceenvironmentdecorator.ProcessingResourceSpecificationResult; |
47 | import de.uka.ipd.sdq.pcm.resultdecorator.resourceenvironmentdecorator.ResourceenvironmentdecoratorFactory; |
48 | import de.uka.ipd.sdq.pcm.resultdecorator.resourceenvironmentdecorator.UtilisationResult; |
49 | import de.uka.ipd.sdq.pcm.seff.ServiceEffectSpecification; |
50 | import de.uka.ipd.sdq.pcm.usagemodel.UsageScenario; |
51 | import de.uka.ipd.sdq.pcmsolver.models.PCMInstance; |
52 | import de.uka.ipd.sdq.pcmsolver.transformations.pcm2lqn.Pcm2LqnHelper; |
53 | |
54 | public abstract class LQNResult extends AbstractPerformanceAnalysisResult implements ILQNResult{ |
55 | |
56 | protected static Logger logger = Logger |
57 | .getLogger("de.uka.ipd.sdq.dsexplore"); |
58 | |
59 | protected double meanResponseTime; |
60 | protected double throughput; |
61 | protected double maxUtilization; |
62 | |
63 | protected double squaredCoeffVariance = 1; |
64 | |
65 | private ResultDecoratorRepository results; |
66 | |
67 | private Map<Criterion, EvaluationAspectWithContext> objectiveToAspects; |
68 | |
69 | private LQNQualityAttributeDeclaration qualityAttributeInfo; |
70 | |
71 | |
72 | |
73 | public LQNResult(PCMInstance pcm, LqnModelType model, |
74 | Criterion criterion, Map<Criterion, EvaluationAspectWithContext> objectiveToAspect, |
75 | LQNQualityAttributeDeclaration qualityAttributeInfo) throws AnalysisFailedException { |
76 | super(pcm); |
77 | try{ |
78 | |
79 | this.objectiveToAspects = objectiveToAspect; |
80 | this.qualityAttributeInfo = qualityAttributeInfo; |
81 | |
82 | if (criterion instanceof UsageScenarioBasedCriterion){ |
83 | this.meanResponseTime = retrieveResponseTimeForUsageScenario(pcm, model,(UsageScenarioBasedCriterion)criterion); |
84 | this.throughput = retrieveThroughputForUsageScenario(pcm, model, (UsageScenarioBasedCriterion)criterion); |
85 | } else if (criterion instanceof EntryLevelSystemCallCriterion){ |
86 | this.meanResponseTime = retrieveResponseTimeForEntryLevelSystemCall(pcm, model,(EntryLevelSystemCallCriterion)criterion); |
87 | this.throughput = retrieveThroughputForEntryLevelSystemCall(pcm, model, (EntryLevelSystemCallCriterion)criterion); |
88 | } else { |
89 | throw new AnalysisFailedException("Unknown criterion type "+criterion.getClass().getName()); |
90 | } |
91 | |
92 | this.results = retrieveResults(pcm, model); |
93 | |
94 | this.maxUtilization = retrieveMaxUtilization(pcm, model, this.results, "CPU"); |
95 | |
96 | } catch (ParseException ex) { |
97 | throw new AnalysisFailedException("Failed to parse string value.", |
98 | ex); |
99 | } |
100 | } |
101 | |
102 | private double retrieveMaxUtilization(PCMInstance pcm, LqnModelType model, |
103 | ResultDecoratorRepository results2, String resourceTypeDescription) { |
104 | double maxUtil = 0; |
105 | for (UtilisationResult utilResult : results2.getUtilisationResults_ResultDecoratorRepository()) { |
106 | if (utilResult instanceof ProcessingResourceSpecificationResult){ |
107 | ProcessingResourceSpecificationResult procResult = (ProcessingResourceSpecificationResult) utilResult; |
108 | // check resource type |
109 | if (procResult.getProcessingResourceSpecification_ProcessingResourceSpecificationResult().getActiveResourceType_ActiveResourceSpecification().getEntityName().contains(resourceTypeDescription)){ |
110 | if (maxUtil < procResult.getResourceUtilisation()){ |
111 | maxUtil = procResult.getResourceUtilisation(); |
112 | } |
113 | } |
114 | |
115 | } |
116 | |
117 | } |
118 | return maxUtil; |
119 | } |
120 | |
121 | /** |
122 | * pcm must be the current candidate's PCM model. |
123 | * |
124 | * @param pcm |
125 | * @param model |
126 | * @return |
127 | * @throws ParseException |
128 | */ |
129 | private ResultDecoratorRepository retrieveResults( |
130 | PCMInstance pcm, LqnModelType model) throws ParseException { |
131 | |
132 | ResultDecoratorRepository repo = ResultdecoratorFactory.eINSTANCE.createResultDecoratorRepository(); |
133 | |
134 | List<ProcessorType> modifiableProcessorList = retrieveResourceEnvironmentResults( |
135 | pcm, model, repo); |
136 | |
137 | retrieveServiceResults(pcm, repo, modifiableProcessorList); |
138 | |
139 | return repo; |
140 | } |
141 | |
142 | private void retrieveServiceResults(PCMInstance pcm, |
143 | ResultDecoratorRepository repo, |
144 | List<ProcessorType> modifiableProcessorList) throws ParseException { |
145 | // retrieve response times |
146 | List<Repository> repositories = pcm.getRepositories(); |
147 | for (Repository repository : repositories) { |
148 | List<RepositoryComponent> repoComponentList = repository.getComponents__Repository(); |
149 | for (RepositoryComponent repositoryComponent : repoComponentList) { |
150 | if (repositoryComponent instanceof BasicComponent){ |
151 | BasicComponent basicComponent = (BasicComponent)repositoryComponent; |
152 | // pass the processor list that has already been used, then it does not contain the resources anymore. |
153 | List<ServiceResult> serviceResultList = getServiceResults(basicComponent, modifiableProcessorList); |
154 | repo.getServiceResult_ResultDecoratorRepository().addAll(serviceResultList); |
155 | } |
156 | } |
157 | } |
158 | } |
159 | |
160 | private List<ProcessorType> retrieveResourceEnvironmentResults( |
161 | PCMInstance pcm, LqnModelType model, ResultDecoratorRepository repo) |
162 | throws ParseException { |
163 | // Retrieve utilisation |
164 | List<ProcessorType> processors = model.getProcessor(); |
165 | List<ResourceContainer> containers = pcm.getResourceEnvironment().getResourceContainer_ResourceEnvironment(); |
166 | |
167 | //create a new list from that we can remove things without modifying the model |
168 | List<ProcessorType> modifiableProcessorList = new ArrayList<ProcessorType>(processors.size()); |
169 | modifiableProcessorList.addAll(processors); |
170 | |
171 | // get results for active resources |
172 | for (ResourceContainer container : containers) { |
173 | for (ProcessingResourceSpecification pcmResource : container.getActiveResourceSpecifications_ResourceContainer()) { |
174 | String processorID = Pcm2LqnHelper.getIdForProcResource(container, pcmResource.getActiveResourceType_ActiveResourceSpecification())+"_Processor"; |
175 | |
176 | ProcessingResourceSpecificationResult result = ResourceenvironmentdecoratorFactory.eINSTANCE.createProcessingResourceSpecificationResult(); |
177 | |
178 | retrieveUtilResultFromLQN(modifiableProcessorList, processorID, |
179 | result); |
180 | //if the resource has not been found, it has not been used and its utilisation is 0 (the default value). |
181 | |
182 | result.setProcessingResourceSpecification_ProcessingResourceSpecificationResult(pcmResource); |
183 | result.setEntityName("Utilisation of "+processorID); |
184 | |
185 | repo.getUtilisationResults_ResultDecoratorRepository().add(result); |
186 | |
187 | } |
188 | } |
189 | |
190 | List<LinkingResource> links = pcm.getResourceEnvironment().getLinkingResources__ResourceEnvironment(); |
191 | |
192 | for (LinkingResource linkingResource : links) { |
193 | String processorID = Pcm2LqnHelper.getIdForProcResource(linkingResource, linkingResource.getCommunicationLinkResourceSpecifications_LinkingResource().getCommunicationLinkResourceType_CommunicationLinkResourceSpecification())+"_Processor"; |
194 | |
195 | LinkingResourceResults result = ResourceenvironmentdecoratorFactory.eINSTANCE.createLinkingResourceResults(); |
196 | |
197 | retrieveUtilResultFromLQN(modifiableProcessorList, processorID, |
198 | result); |
199 | //if the resource has not been found, it has not been used and its utilisation is 0 (the default value). |
200 | |
201 | result.setLinkingResource_LinkingResourceResults(linkingResource); |
202 | result.setEntityName("Utilisation of "+processorID); |
203 | |
204 | repo.getUtilisationResults_ResultDecoratorRepository().add(result); |
205 | } |
206 | |
207 | // results for passive resources |
208 | List<AllocationContext> allAssemblyContexts = EMFHelper.getAllUsedAllocationContexts(pcm.getAllocation()); |
209 | for (AllocationContext allocContext : allAssemblyContexts) { |
210 | AssemblyContext assemblyContext = allocContext.getAssemblyContext_AllocationContext(); |
211 | RepositoryComponent innerComponent = assemblyContext.getEncapsulatedComponent__AssemblyContext(); |
212 | if (innerComponent instanceof BasicComponent){ |
213 | |
214 | BasicComponent basicComponent = (BasicComponent) innerComponent; |
215 | List<PassiveResource> passiveResourceOfComponentList = basicComponent |
216 | .getPassiveResource_BasicComponent(); |
217 | for (PassiveResource passiveResource : passiveResourceOfComponentList) { |
218 | |
219 | String passiveResourceId = Pcm2LqnHelper.getIdForPassiveResource(passiveResource, allocContext); |
220 | |
221 | PassiveResourceResult result = ResourceenvironmentdecoratorFactory.eINSTANCE.createPassiveResourceResult(); |
222 | |
223 | retrieveUtilResultFromLQN(modifiableProcessorList, passiveResourceId, |
224 | result); |
225 | //if the resource has not been found, it has not been used and its utilisation is 0 (the default value). |
226 | |
227 | result.setPassiveResource_PassiveResourceResult(passiveResource); |
228 | result.setAssemblyContext_PassiveResourceResult(assemblyContext); |
229 | result.setEntityName("Utilisation of "+passiveResourceId); |
230 | |
231 | |
232 | repo.getUtilisationResults_ResultDecoratorRepository().add(result); |
233 | |
234 | } |
235 | |
236 | } |
237 | } |
238 | return modifiableProcessorList; |
239 | } |
240 | |
241 | /** |
242 | * Sets utilization, average queue length and average waiting time, if available. |
243 | * @param modifiableProcessorList |
244 | * @param processorID |
245 | * @param result |
246 | * @throws ParseException |
247 | */ |
248 | private void retrieveUtilResultFromLQN( |
249 | List<ProcessorType> modifiableProcessorList, String processorID, |
250 | UtilisationResult result) throws ParseException { |
251 | //use iterator to be allowed to modify the list while searching |
252 | Iterator<ProcessorType> lqnProcessorIterator = modifiableProcessorList.iterator(); |
253 | while (lqnProcessorIterator.hasNext()){ |
254 | ProcessorType lqnProc = lqnProcessorIterator.next(); |
255 | if (lqnProc.getName().contains(processorID)){ |
256 | |
257 | List<OutputResultType> lqnResultProc = lqnProc.getResultProcessor(); |
258 | if (lqnResultProc.size() > 0){ |
259 | |
260 | OutputResultType processorResult = lqnResultProc.get(0); |
261 | |
262 | String utilString = (String)processorResult.getUtilization(); |
263 | result.setResourceUtilisation(LQNUtils |
264 | .convertStringToDouble(utilString)); |
265 | |
266 | // determine waiting times and service times by checking all result entries (contained in the first task) |
267 | double waitingTime = 0; |
268 | double serviceTime = 0; |
269 | |
270 | double totalThroughput = 0; |
271 | |
272 | List<TaskType> resultTask = lqnProc.getTask(); |
273 | if (resultTask.size() > 0){ |
274 | |
275 | List<EntryType> procTaskEntries = resultTask.get(0).getEntry(); |
276 | for (EntryType entryType : procTaskEntries) { |
277 | List<OutputResultType> entryResults = entryType.getResultEntry(); |
278 | if (entryResults.size() > 0 ){ |
279 | |
280 | OutputResultType entryResult = entryResults.get(0); |
281 | String throughputString = (String)entryResult.getThroughput(); |
282 | |
283 | EList<ActivityPhasesType> entryPhaseActivities = entryType.getEntryPhaseActivities().getActivity(); |
284 | if (entryPhaseActivities.size() > 0 ){ |
285 | List<OutputResultType> activityResults = entryPhaseActivities.get(0).getResultActivity(); |
286 | if (activityResults.size() > 0){ |
287 | String waitingTimeString = (String)activityResults.get(0).getProcWaiting(); |
288 | String serviceTimeString = (String)activityResults.get(0).getServiceTime(); |
289 | |
290 | double throughput = LQNUtils.convertStringToDouble(throughputString); |
291 | double entryWaitingTime = LQNUtils.convertStringToDouble(waitingTimeString); |
292 | double entryServiceTime = LQNUtils.convertStringToDouble(serviceTimeString); |
293 | |
294 | // only look at service times greater than one, because the others do not seem to have to wait. |
295 | // weight the current waiting time and service time by the current throughgput to get the overall times. |
296 | if (entryServiceTime > 0){ |
297 | waitingTime = entryWaitingTime * throughput; |
298 | serviceTime = entryServiceTime * throughput; |
299 | totalThroughput += throughput; |
300 | } |
301 | |
302 | } |
303 | } |
304 | } |
305 | } |
306 | |
307 | if (totalThroughput > 0 && serviceTime > 0){ |
308 | // weight waiting time and service time by throughput. |
309 | waitingTime = waitingTime / totalThroughput; |
310 | serviceTime = serviceTime / totalThroughput; |
311 | |
312 | // queue length is queue waiting time / queue service time in average |
313 | result.setAverageQueueLength(waitingTime / serviceTime); |
314 | result.setAverageWaitTime(waitingTime); |
315 | if (result instanceof ProcessingResourceSpecificationResult){ |
316 | ((ProcessingResourceSpecificationResult) result).setDemandedTime(serviceTime); |
317 | } |
318 | } |
319 | } |
320 | |
321 | |
322 | logger.debug("Resource "+processorID+" has utilisation "+utilString+ " and waiting time "+result.getAverageQueueLength()); |
323 | } |
324 | |
325 | //we can remove this element now and do not have to iterate over this one again in the next loop iterations. |
326 | lqnProcessorIterator.remove(); |
327 | } |
328 | } |
329 | } |
330 | |
331 | private List<ServiceResult> getServiceResults(BasicComponent basicComponent, List<ProcessorType> modifiableProcessorResultList) throws ParseException { |
332 | |
333 | |
334 | |
335 | List<ServiceEffectSpecification> seffList = basicComponent.getServiceEffectSpecifications__BasicComponent(); |
336 | List<ServiceResult> results = new ArrayList<ServiceResult>(seffList.size()); |
337 | |
338 | //Create only one service result per SEFF: weight each response time by throughput and then divide by overall throughput. |
339 | for (ServiceEffectSpecification seff : seffList) { |
340 | |
341 | double overallThroughput = 0; |
342 | double responseTimesTimesThroughputSum = 0; |
343 | |
344 | String processorIDRegex = basicComponent.getEntityName() + "_" |
345 | + ((OperationSignature)seff.getDescribedService__SEFF()).getInterface__OperationSignature().getEntityName() + "_" |
346 | + seff.getDescribedService__SEFF().getEntityName(); // + "_*_Processor"; |
347 | |
348 | // Obtain processor, representing the SEFF's overall resource demand |
349 | ProcessorType processor = null; |
350 | Iterator<ProcessorType> itProcessors = modifiableProcessorResultList.iterator(); |
351 | while (itProcessors.hasNext()) { |
352 | ProcessorType proc = itProcessors.next(); |
353 | if (proc.getName().contains(processorIDRegex) && proc.getName().contains("_Processor")) { |
354 | processor = proc; |
355 | itProcessors.remove(); |
356 | |
357 | List<TaskType> taskList = processor.getTask(); |
358 | if (taskList.size() > 0){ |
359 | |
360 | double responseTime = LQNUtils.getResponseTimeOfSubActivities(taskList.get(0)); |
361 | double throughput = 0; |
362 | |
363 | List<EntryType> procTaskEntries = taskList.get(0) |
364 | .getEntry(); |
365 | for (EntryType entryType : procTaskEntries) { |
366 | List<OutputResultType> entryResults = entryType |
367 | .getResultEntry(); |
368 | if (entryResults.size() > 0) { |
369 | |
370 | OutputResultType entryResult = entryResults |
371 | .get(0); |
372 | String throughputString = (String) entryResult |
373 | .getThroughput(); |
374 | throughput += LQNUtils.convertStringToDouble(throughputString); |
375 | } |
376 | } |
377 | |
378 | responseTimesTimesThroughputSum += responseTime * throughput; |
379 | overallThroughput += throughput; |
380 | } |
381 | |
382 | } |
383 | } |
384 | |
385 | // create result if service has been used |
386 | if (overallThroughput > 0) { |
387 | double overallResponseTime = responseTimesTimesThroughputSum |
388 | / overallThroughput; |
389 | |
390 | AllocationServiceResult serviceResult = RepositorydecoratorFactory.eINSTANCE |
391 | .createAllocationServiceResult(); |
392 | |
393 | // FIXME: currently only works with one allocation per seff |
394 | // serviceResult.setAllocationcontext(value); |
395 | serviceResult.setServiceEffectSpecification_ServiceResult(seff); |
396 | serviceResult.setMeanResponseTime(overallResponseTime); |
397 | results.add(serviceResult); |
398 | } |
399 | |
400 | } |
401 | |
402 | return results; |
403 | } |
404 | |
405 | /** |
406 | * @param pcm Can be any PCM instance, only the usage model is used. |
407 | * @param model |
408 | * @param criterion |
409 | * @return |
410 | * @throws ParseException |
411 | * @throws AnalysisFailedException |
412 | */ |
413 | private double retrieveResponseTimeForUsageScenario(PCMInstance pcm, LqnModelType model, UsageScenarioBasedCriterion criterion) throws ParseException, AnalysisFailedException { |
414 | |
415 | ProcessorType processor = getUsageScenarioProcessor(pcm, model, criterion.getUsageScenario()); |
416 | |
417 | if (processor != null) { |
418 | |
419 | if (processor.getTask() != null && processor.getTask().size() > 0) { |
420 | |
421 | // TODO: Can we really assume there is only one task? |
422 | TaskType task = processor.getTask().get(0); |
423 | |
424 | if (task != null){ |
425 | double responseTime = Double.NaN; |
426 | |
427 | if (task.getResultTask().size() > 0){ |
428 | OutputResultType outputResult = task.getResultTask().get(0); |
429 | |
430 | if (outputResult != null) |
431 | responseTime = LQNUtils.getResponseTimeOfSubActivities(task); |
432 | |
433 | if (outputResult.getSquaredCoeffVariation() != null) { |
434 | this.squaredCoeffVariance = LQNUtils |
435 | .convertStringToDouble((String) outputResult |
436 | .getSquaredCoeffVariation()); |
437 | } |
438 | return responseTime; |
439 | } |
440 | } |
441 | |
442 | |
443 | } |
444 | } |
445 | logger.warn("No task or empty task for processor " |
446 | + processor.getName() |
447 | + ". Cannot determine response time, using NaN. Check your models or the LQNResult code."); |
448 | |
449 | return Double.NaN; |
450 | |
451 | } |
452 | |
453 | private double retrieveResponseTimeForEntryLevelSystemCall(PCMInstance pcm, |
454 | LqnModelType model, EntryLevelSystemCallCriterion criterion) throws AnalysisFailedException, ParseException { |
455 | |
456 | ProcessorType usageScenarioProcessor = getUsageScenarioProcessor(pcm, model, criterion); |
457 | |
458 | if (usageScenarioProcessor != null) { |
459 | |
460 | if (usageScenarioProcessor.getTask() != null && usageScenarioProcessor.getTask().size() > 0) { |
461 | |
462 | OutputResultType entryLevelCallActivityResult = getResultActivityForEntryLevelSystemCall( |
463 | criterion, usageScenarioProcessor); |
464 | |
465 | if (entryLevelCallActivityResult != null){ |
466 | return LQNUtils.convertStringToDouble((String) entryLevelCallActivityResult.getServiceTime()); |
467 | } |
468 | |
469 | } |
470 | } |
471 | logger.warn("No task or empty task for processor or empty activity results" |
472 | + usageScenarioProcessor.getName() |
473 | + ". Cannot determine response time, using NaN. Check your models or the LQNResult code."); |
474 | return Double.NaN; |
475 | } |
476 | |
477 | private OutputResultType getResultActivityForEntryLevelSystemCall( |
478 | EntryLevelSystemCallCriterion criterion, |
479 | ProcessorType usageScenarioProcessor) { |
480 | // TODO: Can we really assume there is only one task? |
481 | TaskType task = usageScenarioProcessor.getTask().get(0); |
482 | |
483 | String entryLevelCallId = Pcm2LqnHelper.getIdForEntryLevelSystemCall(criterion.getEntryLevelSystemCall()); |
484 | TreeIterator<Object> allProcessorContentIterator = EcoreUtil.getAllContents(task.getTaskActivities().getActivity(), true); |
485 | OutputResultType activity = findActivityForEntryLevelCallById(entryLevelCallId, |
486 | allProcessorContentIterator); |
487 | |
488 | if (activity == null){ |
489 | |
490 | // the activity might be contained in another processor, e.g. a usage scenario loop processor. |
491 | TreeIterator<Object> allContentIterator = EcoreUtil.getAllContents(usageScenarioProcessor.eContainer(), true); |
492 | activity = findActivityForEntryLevelCallById(entryLevelCallId, allContentIterator); |
493 | } |
494 | |
495 | return activity; |
496 | } |
497 | |
498 | private OutputResultType findActivityForEntryLevelCallById(String entryLevelCallId, |
499 | TreeIterator<Object> allProcessorContentIterator) { |
500 | while (allProcessorContentIterator.hasNext()) { |
501 | Object object = allProcessorContentIterator.next(); |
502 | |
503 | if (object instanceof ActivityDefType && ((ActivityDefType)object).getName().equals(entryLevelCallId)){ |
504 | // this is the entry level system call |
505 | OutputResultType activityResult = ((ActivityDefType)object).getResultActivity().get(0); |
506 | if (activityResult != null){ |
507 | return activityResult; |
508 | } |
509 | } |
510 | }; |
511 | return null; |
512 | } |
513 | |
514 | /** |
515 | * |
516 | * @param pcm Can be any PCM instance, only the usage model is used. |
517 | * @param model |
518 | * @param criterion |
519 | * @return |
520 | * @throws AnalysisFailedException |
521 | * @throws ParseException |
522 | */ |
523 | private double retrieveThroughputForUsageScenario(PCMInstance pcm, |
524 | LqnModelType model, UsageScenarioBasedCriterion criterion) throws AnalysisFailedException, ParseException { |
525 | |
526 | ProcessorType processor = getUsageScenarioProcessor(pcm, model, criterion); |
527 | |
528 | if (processor.getTask() != null && processor.getTask().size() > 0) { |
529 | |
530 | TaskType usageScenarioTask = processor.getTask().get(0); |
531 | if (usageScenarioTask != null && usageScenarioTask.getResultTask().size()>0){ |
532 | OutputResultType outputResult = usageScenarioTask.getResultTask().get(0); |
533 | if (outputResult != null){ |
534 | return LQNUtils.convertStringToDouble((String)outputResult.getThroughput()); |
535 | } |
536 | } |
537 | |
538 | } |
539 | logger.warn("No task or empty task for processor or empty activity results" |
540 | + processor.getName() |
541 | + ". Cannot determine throughput, using NaN. Check your models or the LQNResult code."); |
542 | return Double.NaN; |
543 | |
544 | } |
545 | |
546 | private double retrieveThroughputForEntryLevelSystemCall(PCMInstance pcm, |
547 | LqnModelType model, EntryLevelSystemCallCriterion criterion) throws AnalysisFailedException, ParseException { |
548 | ProcessorType usageScenarioProcessor = getUsageScenarioProcessor(pcm, model, criterion); |
549 | |
550 | if (usageScenarioProcessor != null) { |
551 | |
552 | if (usageScenarioProcessor.getTask() != null && usageScenarioProcessor.getTask().size() > 0) { |
553 | |
554 | OutputResultType entryLevelCallActivityResult = getResultActivityForEntryLevelSystemCall( |
555 | criterion, usageScenarioProcessor); |
556 | |
557 | if (entryLevelCallActivityResult != null){ |
558 | return LQNUtils.convertStringToDouble((String) entryLevelCallActivityResult.getThroughput()); |
559 | } |
560 | |
561 | } |
562 | } |
563 | logger.warn("No task or empty task for processor or empty activity results" |
564 | + usageScenarioProcessor.getName() |
565 | + ". Cannot determine throughput, using NaN. Check your models or the LQNResult code."); |
566 | |
567 | return Double.NaN; |
568 | } |
569 | |
570 | |
571 | private ProcessorType getUsageScenarioProcessor(PCMInstance pcm, |
572 | LqnModelType model, UsageScenarioBasedCriterion criterion) |
573 | throws AnalysisFailedException { |
574 | UsageScenario currentUsageScenaro = null; |
575 | List<UsageScenario> scenarios = pcm.getUsageModel().getUsageScenario_UsageModel(); |
576 | for (UsageScenario usageScenario : scenarios) { |
577 | usageScenario.getId().equals( |
578 | criterion.getUsageScenario().getId()); |
579 | currentUsageScenaro = usageScenario; |
580 | break; |
581 | } |
582 | return getUsageScenarioProcessor(pcm, model, currentUsageScenaro); |
583 | } |
584 | |
585 | private ProcessorType getUsageScenarioProcessor(PCMInstance pcm, |
586 | LqnModelType model, EntryLevelSystemCallCriterion criterion) |
587 | throws AnalysisFailedException { |
588 | UsageScenario currentUsageScenaro = null; |
589 | List<UsageScenario> scenarios = pcm.getUsageModel().getUsageScenario_UsageModel(); |
590 | UsageScenario scenarioToLookFor = getUsageScenarioOfEObject(criterion.getEntryLevelSystemCall()); |
591 | for (UsageScenario usageScenario : scenarios) { |
592 | if (usageScenario.getId().equals(scenarioToLookFor.getId())){ |
593 | currentUsageScenaro = usageScenario; |
594 | break; |
595 | } |
596 | } |
597 | return getUsageScenarioProcessor(pcm, model, currentUsageScenaro); |
598 | } |
599 | |
600 | private UsageScenario getUsageScenarioOfEObject( |
601 | EObject object) { |
602 | EObject container = object.eContainer(); |
603 | if (container instanceof UsageScenario){ |
604 | return (UsageScenario)container; |
605 | } else { |
606 | if (container != null){ |
607 | return getUsageScenarioOfEObject(container); |
608 | } |
609 | } |
610 | return null; |
611 | } |
612 | |
613 | /** |
614 | * Get usage scenario processor for a given {@link UsageScenario}. I am not sure here, |
615 | * but maybe we have to be careful and |
616 | * the {@link UsageScenario} must be the one from the actual PCM model that has been analyzed, |
617 | * not the one referenced from the Criterion. These may differ if they have been loaded |
618 | * separately several Java objects may represent the same model objects. |
619 | * @param pcm |
620 | * @param model |
621 | * @param currentUsageScenaro |
622 | * @return |
623 | * @throws AnalysisFailedException |
624 | */ |
625 | private ProcessorType getUsageScenarioProcessor(PCMInstance pcm, |
626 | LqnModelType model, UsageScenario currentUsageScenaro) |
627 | throws AnalysisFailedException { |
628 | |
629 | if (currentUsageScenaro == null){ |
630 | throw new AnalysisFailedException("Could not analyse LQN results, because the usage scenario from references QML Criterion is not contained in this PCM model's usage model."); |
631 | } |
632 | |
633 | // Retrieve the usage scenario's name used within the result |
634 | // Remove the number from the id as that may have changed(?) XXX |
635 | String scenarioName = Pcm2LqnHelper.getIdForUsageScenario(currentUsageScenaro); |
636 | scenarioName = scenarioName.substring(0, scenarioName.lastIndexOf("_")); |
637 | // \\d stands for a digital number. |
638 | String processorNameRegex = scenarioName + "_\\d+_Processor"; // see class LqnBuilder#getProcessorTypeFromModel(String) |
639 | |
640 | // Obtain processor, representing the usage scenario's overall resource demand |
641 | ProcessorType processor = null; |
642 | Iterator<ProcessorType> itProcessors = model.getProcessor().iterator(); |
643 | while (itProcessors.hasNext()) { |
644 | ProcessorType proc = itProcessors.next(); |
645 | if (proc.getName().matches(processorNameRegex)) { |
646 | processor = proc; |
647 | break; |
648 | } |
649 | } |
650 | if (processor == null) { |
651 | logger.warn("Could not find LQN processor matching name " |
652 | + processorNameRegex + ". Cannot determine response time, using NaN. Check your models or the LQNResult code."); |
653 | |
654 | } |
655 | return processor; |
656 | } |
657 | |
658 | |
659 | /* (non-Javadoc) |
660 | * @see de.uka.ipd.sdq.dsexplore.analysis.lqn.ILQNResult#getMedianValue() |
661 | */ |
662 | public double getMedianValue() { |
663 | return Double.NaN; |
664 | } |
665 | |
666 | @Override |
667 | public double getValueFor(Criterion criterion) { |
668 | EvaluationAspectWithContext aspect = this.objectiveToAspects.get(criterion); |
669 | |
670 | if (aspect != null){ |
671 | if (EcoreUtil.equals(aspect.getDimension(), this.qualityAttributeInfo.getResponseTime())){ |
672 | return this.getMeanValue(); |
673 | } else if (EcoreUtil.equals(aspect.getDimension(), this.qualityAttributeInfo.getThroughput())){ |
674 | return this.getThroughput(); |
675 | } else if (EcoreUtil.equals(aspect.getDimension(), this.qualityAttributeInfo.getMaxUtilization())){ |
676 | return this.getMaxUtilisation(); |
677 | } |
678 | } |
679 | |
680 | logger.warn("Unknown aspect for LQN result, adding NaN."); |
681 | return Double.NaN; |
682 | } |
683 | |
684 | /* (non-Javadoc) |
685 | * @see de.uka.ipd.sdq.dsexplore.analysis.lqn.ILQNResult#getMeanValue() |
686 | */ |
687 | public double getMeanValue() { |
688 | return meanResponseTime; |
689 | } |
690 | |
691 | public double getThroughput() { |
692 | return throughput; |
693 | } |
694 | |
695 | public double getMaxUtilisation(){ |
696 | return this.maxUtilization; |
697 | } |
698 | |
699 | /* (non-Javadoc) |
700 | * @see de.uka.ipd.sdq.dsexplore.analysis.lqn.ILQNResult#getSquaredCoefficientOfVariance() |
701 | */ |
702 | public double getSquaredCoefficientOfVariance(){ |
703 | return this.squaredCoeffVariance; |
704 | } |
705 | |
706 | /* (non-Javadoc) |
707 | * @see de.uka.ipd.sdq.dsexplore.analysis.lqn.ILQNResult#getCoefficientOfVariance() |
708 | */ |
709 | public double getCoefficientOfVariance(){ |
710 | return Math.sqrt(this.squaredCoeffVariance); |
711 | } |
712 | |
713 | /* (non-Javadoc) |
714 | * @see de.uka.ipd.sdq.dsexplore.analysis.lqn.ILQNResult#getVariance() |
715 | */ |
716 | public double getVariance(){ |
717 | double var = this.getSquaredCoefficientOfVariance() * this.getMeanValue() * this.getMeanValue(); |
718 | return var; |
719 | } |
720 | |
721 | /* (non-Javadoc) |
722 | * @see de.uka.ipd.sdq.dsexplore.analysis.lqn.ILQNResult#getStandardDeviation() |
723 | */ |
724 | public double getStandardDeviation(){ |
725 | double std = Math.sqrt(this.getVariance()); |
726 | return std; |
727 | } |
728 | |
729 | |
730 | /* (non-Javadoc) |
731 | * @see de.uka.ipd.sdq.dsexplore.analysis.lqn.ILQNResult#getResults() |
732 | */ |
733 | public ResultDecoratorRepository getResults() { |
734 | return results; |
735 | } |
736 | |
737 | |
738 | } |