xref: /aosp_15_r20/external/toolchain-utils/bestflags/task.py (revision 760c253c1ed00ce9abd48f8546f08516e57485fe)
1*760c253cSXin Li# Copyright 2013 The ChromiumOS Authors
2*760c253cSXin Li# Use of this source code is governed by a BSD-style license that can be
3*760c253cSXin Li# found in the LICENSE file.
4*760c253cSXin Li"""A reproducing entity.
5*760c253cSXin Li
6*760c253cSXin LiPart of the Chrome build flags optimization.
7*760c253cSXin Li
8*760c253cSXin LiThe Task class is used by different modules. Each module fills in the
9*760c253cSXin Licorresponding information into a Task instance. Class Task contains the bit set
10*760c253cSXin Lirepresenting the flags selection. The builder module is responsible for filling
11*760c253cSXin Lithe image and the checksum field of a Task. The executor module will put the
12*760c253cSXin Liexecution output to the execution field.
13*760c253cSXin Li"""
14*760c253cSXin Li
15*760c253cSXin Li__author__ = "[email protected] (Yuheng Long)"
16*760c253cSXin Li
17*760c253cSXin Liimport os
18*760c253cSXin Liimport subprocess
19*760c253cSXin Liimport sys
20*760c253cSXin Lifrom uuid import uuid4
21*760c253cSXin Li
22*760c253cSXin Li
23*760c253cSXin LiBUILD_STAGE = 1
24*760c253cSXin LiTEST_STAGE = 2
25*760c253cSXin Li
26*760c253cSXin Li# Message indicating that the build or test failed.
27*760c253cSXin LiERROR_STRING = "error"
28*760c253cSXin Li
29*760c253cSXin Li# The maximum number of tries a build can have. Some compilations may fail due
30*760c253cSXin Li# to unexpected environment circumstance. This variable defines how many tries
31*760c253cSXin Li# the build should attempt before giving up.
32*760c253cSXin LiBUILD_TRIES = 3
33*760c253cSXin Li
34*760c253cSXin Li# The maximum number of tries a test can have. Some tests may fail due to
35*760c253cSXin Li# unexpected environment circumstance. This variable defines how many tries the
36*760c253cSXin Li# test should attempt before giving up.
37*760c253cSXin LiTEST_TRIES = 3
38*760c253cSXin Li
39*760c253cSXin Li
40*760c253cSXin Li# Create the file/directory if it does not already exist.
41*760c253cSXin Lidef _CreateDirectory(file_name):
42*760c253cSXin Li    directory = os.path.dirname(file_name)
43*760c253cSXin Li    if not os.path.exists(directory):
44*760c253cSXin Li        os.makedirs(directory)
45*760c253cSXin Li
46*760c253cSXin Li
47*760c253cSXin Liclass Task(object):
48*760c253cSXin Li    """A single reproducing entity.
49*760c253cSXin Li
50*760c253cSXin Li    A single test of performance with a particular set of flags. It records the
51*760c253cSXin Li    flag set, the image, the check sum of the image and the cost.
52*760c253cSXin Li    """
53*760c253cSXin Li
54*760c253cSXin Li    # The command that will be used in the build stage to compile the tasks.
55*760c253cSXin Li    BUILD_COMMAND = None
56*760c253cSXin Li    # The command that will be used in the test stage to test the tasks.
57*760c253cSXin Li    TEST_COMMAND = None
58*760c253cSXin Li    # The directory to log the compilation and test results.
59*760c253cSXin Li    LOG_DIRECTORY = None
60*760c253cSXin Li
61*760c253cSXin Li    @staticmethod
62*760c253cSXin Li    def InitLogCommand(build_command, test_command, log_directory):
63*760c253cSXin Li        """Set up the build and test command for the task and the log directory.
64*760c253cSXin Li
65*760c253cSXin Li        This framework is generic. It lets the client specify application specific
66*760c253cSXin Li        compile and test methods by passing different build_command and
67*760c253cSXin Li        test_command.
68*760c253cSXin Li
69*760c253cSXin Li        Args:
70*760c253cSXin Li          build_command: The command that will be used in the build stage to compile
71*760c253cSXin Li            this task.
72*760c253cSXin Li          test_command: The command that will be used in the test stage to test this
73*760c253cSXin Li            task.
74*760c253cSXin Li          log_directory: The directory to log the compilation and test results.
75*760c253cSXin Li        """
76*760c253cSXin Li
77*760c253cSXin Li        Task.BUILD_COMMAND = build_command
78*760c253cSXin Li        Task.TEST_COMMAND = test_command
79*760c253cSXin Li        Task.LOG_DIRECTORY = log_directory
80*760c253cSXin Li
81*760c253cSXin Li    def __init__(self, flag_set):
82*760c253cSXin Li        """Set up the optimization flag selection for this task.
83*760c253cSXin Li
84*760c253cSXin Li        Args:
85*760c253cSXin Li          flag_set: The optimization flag set that is encapsulated by this task.
86*760c253cSXin Li        """
87*760c253cSXin Li
88*760c253cSXin Li        self._flag_set = flag_set
89*760c253cSXin Li
90*760c253cSXin Li        # A unique identifier that distinguishes this task from other tasks.
91*760c253cSXin Li        self._task_identifier = uuid4()
92*760c253cSXin Li
93*760c253cSXin Li        self._log_path = (Task.LOG_DIRECTORY, self._task_identifier)
94*760c253cSXin Li
95*760c253cSXin Li        # Initiate the hash value. The hash value is used so as not to recompute it
96*760c253cSXin Li        # every time the hash method is called.
97*760c253cSXin Li        self._hash_value = None
98*760c253cSXin Li
99*760c253cSXin Li        # Indicate that the task has not been compiled/tested.
100*760c253cSXin Li        self._build_cost = None
101*760c253cSXin Li        self._exe_cost = None
102*760c253cSXin Li        self._checksum = None
103*760c253cSXin Li        self._image = None
104*760c253cSXin Li        self._file_length = None
105*760c253cSXin Li        self._text_length = None
106*760c253cSXin Li
107*760c253cSXin Li    def __eq__(self, other):
108*760c253cSXin Li        """Test whether two tasks are equal.
109*760c253cSXin Li
110*760c253cSXin Li        Two tasks are equal if their flag_set are equal.
111*760c253cSXin Li
112*760c253cSXin Li        Args:
113*760c253cSXin Li          other: The other task with which this task is tested equality.
114*760c253cSXin Li        Returns:
115*760c253cSXin Li          True if the encapsulated flag sets are equal.
116*760c253cSXin Li        """
117*760c253cSXin Li        if isinstance(other, Task):
118*760c253cSXin Li            return self.GetFlags() == other.GetFlags()
119*760c253cSXin Li        return False
120*760c253cSXin Li
121*760c253cSXin Li    def __hash__(self):
122*760c253cSXin Li        if self._hash_value is None:
123*760c253cSXin Li            # Cache the hash value of the flags, so as not to recompute them.
124*760c253cSXin Li            self._hash_value = hash(self._flag_set)
125*760c253cSXin Li        return self._hash_value
126*760c253cSXin Li
127*760c253cSXin Li    def GetIdentifier(self, stage):
128*760c253cSXin Li        """Get the identifier of the task in the stage.
129*760c253cSXin Li
130*760c253cSXin Li        The flag set uniquely identifies a task in the build stage. The checksum of
131*760c253cSXin Li        the image of the task uniquely identifies the task in the test stage.
132*760c253cSXin Li
133*760c253cSXin Li        Args:
134*760c253cSXin Li          stage: The stage (build/test) in which this method is called.
135*760c253cSXin Li        Returns:
136*760c253cSXin Li          Return the flag set in build stage and return the checksum in test stage.
137*760c253cSXin Li        """
138*760c253cSXin Li
139*760c253cSXin Li        # Define the dictionary for different stage function lookup.
140*760c253cSXin Li        get_identifier_functions = {
141*760c253cSXin Li            BUILD_STAGE: self.FormattedFlags,
142*760c253cSXin Li            TEST_STAGE: self.__GetCheckSum,
143*760c253cSXin Li        }
144*760c253cSXin Li
145*760c253cSXin Li        assert stage in get_identifier_functions
146*760c253cSXin Li        return get_identifier_functions[stage]()
147*760c253cSXin Li
148*760c253cSXin Li    def GetResult(self, stage):
149*760c253cSXin Li        """Get the performance results of the task in the stage.
150*760c253cSXin Li
151*760c253cSXin Li        Args:
152*760c253cSXin Li          stage: The stage (build/test) in which this method is called.
153*760c253cSXin Li        Returns:
154*760c253cSXin Li          Performance results.
155*760c253cSXin Li        """
156*760c253cSXin Li
157*760c253cSXin Li        # Define the dictionary for different stage function lookup.
158*760c253cSXin Li        get_result_functions = {
159*760c253cSXin Li            BUILD_STAGE: self.__GetBuildResult,
160*760c253cSXin Li            TEST_STAGE: self.GetTestResult,
161*760c253cSXin Li        }
162*760c253cSXin Li
163*760c253cSXin Li        assert stage in get_result_functions
164*760c253cSXin Li
165*760c253cSXin Li        return get_result_functions[stage]()
166*760c253cSXin Li
167*760c253cSXin Li    def SetResult(self, stage, result):
168*760c253cSXin Li        """Set the performance results of the task in the stage.
169*760c253cSXin Li
170*760c253cSXin Li        This method is called by the pipeling_worker to set the results for
171*760c253cSXin Li        duplicated tasks.
172*760c253cSXin Li
173*760c253cSXin Li        Args:
174*760c253cSXin Li          stage: The stage (build/test) in which this method is called.
175*760c253cSXin Li          result: The performance results of the stage.
176*760c253cSXin Li        """
177*760c253cSXin Li
178*760c253cSXin Li        # Define the dictionary for different stage function lookup.
179*760c253cSXin Li        set_result_functions = {
180*760c253cSXin Li            BUILD_STAGE: self.__SetBuildResult,
181*760c253cSXin Li            TEST_STAGE: self.__SetTestResult,
182*760c253cSXin Li        }
183*760c253cSXin Li
184*760c253cSXin Li        assert stage in set_result_functions
185*760c253cSXin Li
186*760c253cSXin Li        set_result_functions[stage](result)
187*760c253cSXin Li
188*760c253cSXin Li    def Done(self, stage):
189*760c253cSXin Li        """Check whether the stage is done.
190*760c253cSXin Li
191*760c253cSXin Li        Args:
192*760c253cSXin Li          stage: The stage to be checked, build or test.
193*760c253cSXin Li        Returns:
194*760c253cSXin Li          True if the stage is done.
195*760c253cSXin Li        """
196*760c253cSXin Li
197*760c253cSXin Li        # Define the dictionary for different result string lookup.
198*760c253cSXin Li        done_string = {
199*760c253cSXin Li            BUILD_STAGE: self._build_cost,
200*760c253cSXin Li            TEST_STAGE: self._exe_cost,
201*760c253cSXin Li        }
202*760c253cSXin Li
203*760c253cSXin Li        assert stage in done_string
204*760c253cSXin Li
205*760c253cSXin Li        return done_string[stage] is not None
206*760c253cSXin Li
207*760c253cSXin Li    def Work(self, stage):
208*760c253cSXin Li        """Perform the task.
209*760c253cSXin Li
210*760c253cSXin Li        Args:
211*760c253cSXin Li          stage: The stage in which the task is performed, compile or test.
212*760c253cSXin Li        """
213*760c253cSXin Li
214*760c253cSXin Li        # Define the dictionary for different stage function lookup.
215*760c253cSXin Li        work_functions = {BUILD_STAGE: self.__Compile, TEST_STAGE: self.__Test}
216*760c253cSXin Li
217*760c253cSXin Li        assert stage in work_functions
218*760c253cSXin Li
219*760c253cSXin Li        work_functions[stage]()
220*760c253cSXin Li
221*760c253cSXin Li    def FormattedFlags(self):
222*760c253cSXin Li        """Format the optimization flag set of this task.
223*760c253cSXin Li
224*760c253cSXin Li        Returns:
225*760c253cSXin Li          The formatted optimization flag set that is encapsulated by this task.
226*760c253cSXin Li        """
227*760c253cSXin Li        return str(self._flag_set.FormattedForUse())
228*760c253cSXin Li
229*760c253cSXin Li    def GetFlags(self):
230*760c253cSXin Li        """Get the optimization flag set of this task.
231*760c253cSXin Li
232*760c253cSXin Li        Returns:
233*760c253cSXin Li          The optimization flag set that is encapsulated by this task.
234*760c253cSXin Li        """
235*760c253cSXin Li
236*760c253cSXin Li        return self._flag_set
237*760c253cSXin Li
238*760c253cSXin Li    def __GetCheckSum(self):
239*760c253cSXin Li        """Get the compilation image checksum of this task.
240*760c253cSXin Li
241*760c253cSXin Li        Returns:
242*760c253cSXin Li          The compilation image checksum of this task.
243*760c253cSXin Li        """
244*760c253cSXin Li
245*760c253cSXin Li        # The checksum should be computed before this method is called.
246*760c253cSXin Li        assert self._checksum is not None
247*760c253cSXin Li        return self._checksum
248*760c253cSXin Li
249*760c253cSXin Li    def __Compile(self):
250*760c253cSXin Li        """Run a compile.
251*760c253cSXin Li
252*760c253cSXin Li        This method compile an image using the present flags, get the image,
253*760c253cSXin Li        test the existent of the image and gathers monitoring information, and sets
254*760c253cSXin Li        the internal cost (fitness) for this set of flags.
255*760c253cSXin Li        """
256*760c253cSXin Li
257*760c253cSXin Li        # Format the flags as a string as input to compile command. The unique
258*760c253cSXin Li        # identifier is passed to the compile command. If concurrent processes are
259*760c253cSXin Li        # used to compile different tasks, these processes can use the identifier to
260*760c253cSXin Li        # write to different file.
261*760c253cSXin Li        flags = self._flag_set.FormattedForUse()
262*760c253cSXin Li        command = "%s %s %s" % (
263*760c253cSXin Li            Task.BUILD_COMMAND,
264*760c253cSXin Li            " ".join(flags),
265*760c253cSXin Li            self._task_identifier,
266*760c253cSXin Li        )
267*760c253cSXin Li
268*760c253cSXin Li        # Try BUILD_TRIES number of times before confirming that the build fails.
269*760c253cSXin Li        for _ in range(BUILD_TRIES):
270*760c253cSXin Li            try:
271*760c253cSXin Li                # Execute the command and get the execution status/results.
272*760c253cSXin Li                p = subprocess.Popen(
273*760c253cSXin Li                    command.split(),
274*760c253cSXin Li                    stdout=subprocess.PIPE,
275*760c253cSXin Li                    stderr=subprocess.PIPE,
276*760c253cSXin Li                )
277*760c253cSXin Li                (out, err) = p.communicate()
278*760c253cSXin Li
279*760c253cSXin Li                if out:
280*760c253cSXin Li                    out = out.strip()
281*760c253cSXin Li                    if out != ERROR_STRING:
282*760c253cSXin Li                        # Each build results contains the checksum of the result image, the
283*760c253cSXin Li                        # performance cost of the build, the compilation image, the length
284*760c253cSXin Li                        # of the build, and the length of the text section of the build.
285*760c253cSXin Li                        (
286*760c253cSXin Li                            checksum,
287*760c253cSXin Li                            cost,
288*760c253cSXin Li                            image,
289*760c253cSXin Li                            file_length,
290*760c253cSXin Li                            text_length,
291*760c253cSXin Li                        ) = out.split()
292*760c253cSXin Li                        # Build successfully.
293*760c253cSXin Li                        break
294*760c253cSXin Li
295*760c253cSXin Li                # Build failed.
296*760c253cSXin Li                cost = ERROR_STRING
297*760c253cSXin Li            except _:
298*760c253cSXin Li                # If there is exception getting the cost information of the build, the
299*760c253cSXin Li                # build failed.
300*760c253cSXin Li                cost = ERROR_STRING
301*760c253cSXin Li
302*760c253cSXin Li        # Convert the build cost from String to integer. The build cost is used to
303*760c253cSXin Li        # compare a task with another task. Set the build cost of the failing task
304*760c253cSXin Li        # to the max integer. The for loop will keep trying until either there is a
305*760c253cSXin Li        # success or BUILD_TRIES number of tries have been conducted.
306*760c253cSXin Li        self._build_cost = sys.maxint if cost == ERROR_STRING else float(cost)
307*760c253cSXin Li
308*760c253cSXin Li        self._checksum = checksum
309*760c253cSXin Li        self._file_length = file_length
310*760c253cSXin Li        self._text_length = text_length
311*760c253cSXin Li        self._image = image
312*760c253cSXin Li
313*760c253cSXin Li        self.__LogBuildCost(err)
314*760c253cSXin Li
315*760c253cSXin Li    def __Test(self):
316*760c253cSXin Li        """__Test the task against benchmark(s) using the input test command."""
317*760c253cSXin Li
318*760c253cSXin Li        # Ensure that the task is compiled before being tested.
319*760c253cSXin Li        assert self._image is not None
320*760c253cSXin Li
321*760c253cSXin Li        # If the task does not compile, no need to test.
322*760c253cSXin Li        if self._image == ERROR_STRING:
323*760c253cSXin Li            self._exe_cost = ERROR_STRING
324*760c253cSXin Li            return
325*760c253cSXin Li
326*760c253cSXin Li        # The unique identifier is passed to the test command. If concurrent
327*760c253cSXin Li        # processes are used to compile different tasks, these processes can use the
328*760c253cSXin Li        # identifier to write to different file.
329*760c253cSXin Li        command = "%s %s %s" % (
330*760c253cSXin Li            Task.TEST_COMMAND,
331*760c253cSXin Li            self._image,
332*760c253cSXin Li            self._task_identifier,
333*760c253cSXin Li        )
334*760c253cSXin Li
335*760c253cSXin Li        # Try TEST_TRIES number of times before confirming that the build fails.
336*760c253cSXin Li        for _ in range(TEST_TRIES):
337*760c253cSXin Li            try:
338*760c253cSXin Li                p = subprocess.Popen(
339*760c253cSXin Li                    command.split(),
340*760c253cSXin Li                    stdout=subprocess.PIPE,
341*760c253cSXin Li                    stderr=subprocess.PIPE,
342*760c253cSXin Li                )
343*760c253cSXin Li                (out, err) = p.communicate()
344*760c253cSXin Li
345*760c253cSXin Li                if out:
346*760c253cSXin Li                    out = out.strip()
347*760c253cSXin Li                    if out != ERROR_STRING:
348*760c253cSXin Li                        # The test results contains the performance cost of the test.
349*760c253cSXin Li                        cost = out
350*760c253cSXin Li                        # Test successfully.
351*760c253cSXin Li                        break
352*760c253cSXin Li
353*760c253cSXin Li                # Test failed.
354*760c253cSXin Li                cost = ERROR_STRING
355*760c253cSXin Li            except _:
356*760c253cSXin Li                # If there is exception getting the cost information of the test, the
357*760c253cSXin Li                # test failed. The for loop will keep trying until either there is a
358*760c253cSXin Li                # success or TEST_TRIES number of tries have been conducted.
359*760c253cSXin Li                cost = ERROR_STRING
360*760c253cSXin Li
361*760c253cSXin Li        self._exe_cost = sys.maxint if (cost == ERROR_STRING) else float(cost)
362*760c253cSXin Li
363*760c253cSXin Li        self.__LogTestCost(err)
364*760c253cSXin Li
365*760c253cSXin Li    def __SetBuildResult(
366*760c253cSXin Li        self, (checksum, build_cost, image, file_length, text_length)
367*760c253cSXin Li    ):
368*760c253cSXin Li        self._checksum = checksum
369*760c253cSXin Li        self._build_cost = build_cost
370*760c253cSXin Li        self._image = image
371*760c253cSXin Li        self._file_length = file_length
372*760c253cSXin Li        self._text_length = text_length
373*760c253cSXin Li
374*760c253cSXin Li    def __GetBuildResult(self):
375*760c253cSXin Li        return (
376*760c253cSXin Li            self._checksum,
377*760c253cSXin Li            self._build_cost,
378*760c253cSXin Li            self._image,
379*760c253cSXin Li            self._file_length,
380*760c253cSXin Li            self._text_length,
381*760c253cSXin Li        )
382*760c253cSXin Li
383*760c253cSXin Li    def GetTestResult(self):
384*760c253cSXin Li        return self._exe_cost
385*760c253cSXin Li
386*760c253cSXin Li    def __SetTestResult(self, exe_cost):
387*760c253cSXin Li        self._exe_cost = exe_cost
388*760c253cSXin Li
389*760c253cSXin Li    def LogSteeringCost(self):
390*760c253cSXin Li        """Log the performance results for the task.
391*760c253cSXin Li
392*760c253cSXin Li        This method is called by the steering stage and this method writes the
393*760c253cSXin Li        results out to a file. The results include the build and the test results.
394*760c253cSXin Li        """
395*760c253cSXin Li
396*760c253cSXin Li        steering_log = "%s/%s/steering.txt" % self._log_path
397*760c253cSXin Li
398*760c253cSXin Li        _CreateDirectory(steering_log)
399*760c253cSXin Li
400*760c253cSXin Li        with open(steering_log, "w") as out_file:
401*760c253cSXin Li            # Include the build and the test results.
402*760c253cSXin Li            steering_result = (
403*760c253cSXin Li                self._flag_set,
404*760c253cSXin Li                self._checksum,
405*760c253cSXin Li                self._build_cost,
406*760c253cSXin Li                self._image,
407*760c253cSXin Li                self._file_length,
408*760c253cSXin Li                self._text_length,
409*760c253cSXin Li                self._exe_cost,
410*760c253cSXin Li            )
411*760c253cSXin Li
412*760c253cSXin Li            # Write out the result in the comma-separated format (CSV).
413*760c253cSXin Li            out_file.write("%s,%s,%s,%s,%s,%s,%s\n" % steering_result)
414*760c253cSXin Li
415*760c253cSXin Li    def __LogBuildCost(self, log):
416*760c253cSXin Li        """Log the build results for the task.
417*760c253cSXin Li
418*760c253cSXin Li        The build results include the compilation time of the build, the result
419*760c253cSXin Li        image, the checksum, the file length and the text length of the image.
420*760c253cSXin Li        The file length of the image includes the length of the file of the image.
421*760c253cSXin Li        The text length only includes the length of the text section of the image.
422*760c253cSXin Li
423*760c253cSXin Li        Args:
424*760c253cSXin Li          log: The build log of this task.
425*760c253cSXin Li        """
426*760c253cSXin Li
427*760c253cSXin Li        build_result_log = "%s/%s/build.txt" % self._log_path
428*760c253cSXin Li
429*760c253cSXin Li        _CreateDirectory(build_result_log)
430*760c253cSXin Li
431*760c253cSXin Li        with open(build_result_log, "w") as out_file:
432*760c253cSXin Li            build_result = (
433*760c253cSXin Li                self._flag_set,
434*760c253cSXin Li                self._build_cost,
435*760c253cSXin Li                self._image,
436*760c253cSXin Li                self._checksum,
437*760c253cSXin Li                self._file_length,
438*760c253cSXin Li                self._text_length,
439*760c253cSXin Li            )
440*760c253cSXin Li
441*760c253cSXin Li            # Write out the result in the comma-separated format (CSV).
442*760c253cSXin Li            out_file.write("%s,%s,%s,%s,%s,%s\n" % build_result)
443*760c253cSXin Li
444*760c253cSXin Li        # The build information about running the build.
445*760c253cSXin Li        build_run_log = "%s/%s/build_log.txt" % self._log_path
446*760c253cSXin Li        _CreateDirectory(build_run_log)
447*760c253cSXin Li
448*760c253cSXin Li        with open(build_run_log, "w") as out_log_file:
449*760c253cSXin Li            # Write out the execution information.
450*760c253cSXin Li            out_log_file.write("%s" % log)
451*760c253cSXin Li
452*760c253cSXin Li    def __LogTestCost(self, log):
453*760c253cSXin Li        """Log the test results for the task.
454*760c253cSXin Li
455*760c253cSXin Li        The test results include the runtime execution time of the test.
456*760c253cSXin Li
457*760c253cSXin Li        Args:
458*760c253cSXin Li          log: The test log of this task.
459*760c253cSXin Li        """
460*760c253cSXin Li
461*760c253cSXin Li        test_log = "%s/%s/test.txt" % self._log_path
462*760c253cSXin Li
463*760c253cSXin Li        _CreateDirectory(test_log)
464*760c253cSXin Li
465*760c253cSXin Li        with open(test_log, "w") as out_file:
466*760c253cSXin Li            test_result = (self._flag_set, self._checksum, self._exe_cost)
467*760c253cSXin Li
468*760c253cSXin Li            # Write out the result in the comma-separated format (CSV).
469*760c253cSXin Li            out_file.write("%s,%s,%s\n" % test_result)
470*760c253cSXin Li
471*760c253cSXin Li        # The execution information about running the test.
472*760c253cSXin Li        test_run_log = "%s/%s/test_log.txt" % self._log_path
473*760c253cSXin Li
474*760c253cSXin Li        _CreateDirectory(test_run_log)
475*760c253cSXin Li
476*760c253cSXin Li        with open(test_run_log, "w") as out_log_file:
477*760c253cSXin Li            # Append the test log information.
478*760c253cSXin Li            out_log_file.write("%s" % log)
479*760c253cSXin Li
480*760c253cSXin Li    def IsImproved(self, other):
481*760c253cSXin Li        """Compare the current task with another task.
482*760c253cSXin Li
483*760c253cSXin Li        Args:
484*760c253cSXin Li          other: The other task against which the current task is compared.
485*760c253cSXin Li
486*760c253cSXin Li        Returns:
487*760c253cSXin Li          True if this task has improvement upon the other task.
488*760c253cSXin Li        """
489*760c253cSXin Li
490*760c253cSXin Li        # The execution costs must have been initiated.
491*760c253cSXin Li        assert self._exe_cost is not None
492*760c253cSXin Li        assert other.GetTestResult() is not None
493*760c253cSXin Li
494*760c253cSXin Li        return self._exe_cost < other.GetTestResult()
495