Skip to content

Commit d6ce764

Browse files
Handle issue metadata properly (#4174)
Since it's a heterogenous dictionary, we must store it as a string representation of a json dict (as unwieldy as this is, it's the only correct way to do this). Fixes: https://pantheon.corp.google.com/errors/detail/CODtycKwtN-pGA;filter=%5B%22Type%22%5D;time=P30D;locations=global?e=-13802955&invt=AbZgCw&mods=logs_tg_prod&project=google.com:cluster-fuzz ``` Bar chart Sample stack trace Parsed Raw TypeError: bad argument type for built-in operation at .update ( <frozen _collections_abc>:949 ) at .find_fixed_range ( /mnt/scratch0/clusterfuzz/src/clusterfuzz/_internal/bot/tasks/utasks/progression_task.py:567 ) at .utask_main ( /mnt/scratch0/clusterfuzz/src/clusterfuzz/_internal/bot/tasks/utasks/progression_task.py:671 ) at .uworker_main_no_io ( /mnt/scratch0/clusterfuzz/src/clusterfuzz/_internal/bot/tasks/utasks/__init__.py:211 ) at .execute_locally ( /mnt/scratch0/clusterfuzz/src/clusterfuzz/_internal/bot/tasks/task_types.py:63 ) at .execute ( /mnt/scratch0/clusterfuzz/src/clusterfuzz/_internal/bot/tasks/task_types.py:127 ) at .run_command ( /mnt/scratch0/clusterfuzz/src/clusterfuzz/_internal/bot/tasks/commands.py:218 ) at .process_command_impl ( /mnt/scratch0/clusterfuzz/src/clusterfuzz/_internal/bot/tasks/commands.py:429 ) at .wrapper ( /mnt/scratch0/clusterfuzz/src/clusterfuzz/_internal/bot/tasks/commands.py:159 ) at .process_command ( /mnt/scratch0/clusterfuzz/src/clusterfuzz/_internal/bot/tasks/commands.py:248 ) at .task_loop ( /mnt/scratch0/clusterfuzz/src/python/bot/startup/run_bot.py:146 ) ```
1 parent 5f093bf commit d6ce764

File tree

6 files changed

+40
-32
lines changed

6 files changed

+40
-32
lines changed

src/clusterfuzz/_internal/bot/tasks/utasks/analyze_task.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
"""Analyze task for handling user uploads."""
1515

1616
import datetime
17+
import json
18+
from typing import Dict
1719
from typing import Optional
1820

1921
from clusterfuzz._internal.base import tasks
@@ -36,7 +38,8 @@
3638
from clusterfuzz._internal.system import environment
3739

3840

39-
def _add_default_issue_metadata(testcase, fuzz_target_metadata):
41+
def _add_default_issue_metadata(testcase: data_types.Testcase,
42+
fuzz_target_metadata: Dict):
4043
"""Adds the default issue metadata (e.g. components, labels) to testcase."""
4144
testcase_metadata = testcase.get_metadata()
4245
for key, default_value in fuzz_target_metadata.items():
@@ -420,7 +423,7 @@ def utask_main(uworker_input):
420423
analyze_task_output=analyze_task_output,
421424
test_timeout=test_timeout,
422425
crash_time=crash_time,
423-
issue_metadata=fuzz_target_metadata)
426+
issue_metadata=json.dumps(fuzz_target_metadata))
424427

425428

426429
def test_for_reproducibility(fuzz_target, testcase, testcase_file_path, state,
@@ -565,7 +568,7 @@ def utask_postprocess(output):
565568
testcase_upload_metadata.security_flag = testcase.security_flag
566569
testcase_upload_metadata.put()
567570

568-
_add_default_issue_metadata(testcase, output.issue_metadata)
571+
_add_default_issue_metadata(testcase, json.loads(output.issue_metadata))
569572
logs.info('Creating post-analyze tasks.')
570573

571574
# Create tasks to

src/clusterfuzz/_internal/bot/tasks/utasks/corpus_pruning_task.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import collections
1717
import datetime
18+
import json
1819
import os
1920
import random
2021
import shutil
@@ -842,7 +843,7 @@ def _process_corpus_crashes(output: uworker_msg_pb2.Output): # pylint: disable=
842843
corpus_pruning_output.fuzzer_binary_name)
843844

844845
if output.issue_metadata:
845-
for key, value in output.issue_metadata.items():
846+
for key, value in json.loads(output.issue_metadata).items():
846847
testcase.set_metadata(key, value, update_testcase=False)
847848

848849
testcase.put()
@@ -1032,7 +1033,7 @@ def utask_main(uworker_input):
10321033
crash_revision=result.revision,
10331034
crashes=_extract_corpus_crashes(result),
10341035
corpus_backup_uploaded=bool(result.coverage_info.corpus_location)),
1035-
issue_metadata=issue_metadata)
1036+
issue_metadata=json.dumps(issue_metadata))
10361037
_fill_cross_pollination_stats(result.cross_pollination_stats,
10371038
uworker_output)
10381039
except Exception as e:

src/clusterfuzz/_internal/bot/tasks/utasks/progression_task.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
# limitations under the License.
1414
"""Test to see if test cases are fixed."""
1515

16+
import json
1617
import time
18+
from typing import Dict
1719
from typing import List
1820

1921
from clusterfuzz._internal.base import bisection
@@ -297,15 +299,15 @@ def _check_fixed_for_custom_binary(testcase: data_types.Testcase,
297299
return uworker_msg_pb2.Output(progression_task_output=progression_task_output) # pylint: disable=no-member
298300

299301

300-
def _update_issue_metadata(testcase, metadata):
302+
def _update_issue_metadata(testcase: data_types.Testcase, metadata: Dict):
301303
if not metadata:
302304
return
303305

304306
for key, value in metadata.items():
305307
old_value = testcase.get_metadata(key)
306308
if old_value != value:
307-
logs.info('Updating issue metadata for {} from {} to {}.'.format(
308-
key, old_value, value))
309+
logs.info(
310+
f'Updating issue metadata for {key} from {old_value} to {value}.')
309311
testcase.set_metadata(key, value)
310312

311313

@@ -570,7 +572,7 @@ def find_fixed_range(uworker_input):
570572
last_tested_crash_stacktrace)
571573
return uworker_msg_pb2.Output(
572574
progression_task_output=progression_task_output,
573-
issue_metadata=issue_metadata)
575+
issue_metadata=json.dumps(issue_metadata))
574576

575577
# Verify that we do crash in the min revision. This is assumed to be true
576578
# while we are doing the bisect.
@@ -585,7 +587,7 @@ def find_fixed_range(uworker_input):
585587
progression_task_output.crash_revision = int(min_revision)
586588
return uworker_msg_pb2.Output( # pylint: disable=no-member
587589
progression_task_output=progression_task_output,
588-
issue_metadata=issue_metadata,
590+
issue_metadata=json.dumps(issue_metadata),
589591
error_message=error_message,
590592
error_type=uworker_msg_pb2.ErrorType.PROGRESSION_NO_CRASH) # pylint: disable=no-member
591593

@@ -607,7 +609,7 @@ def find_fixed_range(uworker_input):
607609
progression_task_output.max_revision = int(max_revision)
608610
return uworker_msg_pb2.Output( # pylint: disable=no-member
609611
progression_task_output=progression_task_output,
610-
issue_metadata=issue_metadata)
612+
issue_metadata=json.dumps(issue_metadata))
611613

612614
# Occasionally, we get into this bad state. It seems to be related to test
613615
# cases with flaky stacks, but the exact cause is unknown.
@@ -622,7 +624,7 @@ def find_fixed_range(uworker_input):
622624
progression_task_output.last_progression_max = int(last_progression_max)
623625
return uworker_msg_pb2.Output( # pylint: disable=no-member
624626
progression_task_output=progression_task_output,
625-
issue_metadata=issue_metadata,
627+
issue_metadata=json.dumps(issue_metadata),
626628
error_type=uworker_msg_pb2.ErrorType.PROGRESSION_BAD_STATE_MIN_MAX) # pylint: disable=no-member
627629

628630
# Test the middle revision of our range.
@@ -662,7 +664,7 @@ def find_fixed_range(uworker_input):
662664
progression_task_output.last_progression_max = last_progression_max
663665
return uworker_msg_pb2.Output( # pylint: disable=no-member
664666
error_message=error_message,
665-
issue_metadata=issue_metadata,
667+
issue_metadata=json.dumps(issue_metadata),
666668
progression_task_output=progression_task_output,
667669
error_type=uworker_msg_pb2.ErrorType.PROGRESSION_TIMEOUT) # pylint: disable=no-member
668670

@@ -705,7 +707,7 @@ def utask_postprocess(output: uworker_msg_pb2.Output): # pylint: disable=no-mem
705707
task_output = None
706708

707709
if output.issue_metadata:
708-
_update_issue_metadata(testcase, output.issue_metadata)
710+
_update_issue_metadata(testcase, json.loads(output.issue_metadata))
709711

710712
if output.HasField('progression_task_output'):
711713
task_output = output.progression_task_output

src/clusterfuzz/_internal/protos/uworker_msg.proto

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,6 @@ message Output {
395395
optional SymbolizeTaskOutput symbolize_task_output = 13;
396396
optional VariantTaskOutput variant_task_output = 14;
397397
optional CorpusPruningTaskOutput corpus_pruning_task_output = 16;
398-
map<string,string> issue_metadata = 17;
398+
optional string issue_metadata = 20;
399399
optional string error_message = 15;
400400
}

src/clusterfuzz/_internal/protos/uworker_msg_pb2.py

Lines changed: 4 additions & 8 deletions
Large diffs are not rendered by default.

src/clusterfuzz/_internal/tests/core/bot/tasks/utasks/uworker_io_test.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"""Tests for uworker_io."""
1515

1616
import datetime
17+
import json
1718
import os
1819
import tempfile
1920
import unittest
@@ -347,15 +348,6 @@ def test_list_update(self):
347348
deserialized = uworker_io.deserialize_uworker_input(wire_format)
348349
self.assertEqual(deserialized.analyze_task_input.bad_revisions, [0, 1])
349350

350-
def test_map_update(self):
351-
"""Tests that updating a map works."""
352-
output = uworker_msg_pb2.Output(issue_metadata={'a': 'b', 'c': 'd'})
353-
output.issue_metadata.clear()
354-
output.issue_metadata.update({'e': 'f'})
355-
wire_format = uworker_io.serialize_uworker_output(output)
356-
deserialized = uworker_io.deserialize_uworker_output(wire_format)
357-
self.assertEqual(deserialized.issue_metadata, {'e': 'f'})
358-
359351
def test_submessage_references(self):
360352
"""Tests that updating a submessage works both when directly reading from
361353
uworker_input and from reading from it once it has been serialized and
@@ -381,3 +373,17 @@ def test_unset_a_message_field(self):
381373
wire_format = uworker_io.serialize_uworker_input(uworker_input)
382374
deserialized = uworker_io.deserialize_uworker_input(wire_format)
383375
self.assertFalse(deserialized.HasField('analyze_task_input'))
376+
377+
def test_issue_metadata_field(self):
378+
"""Tests issue_metadata a serialized json string."""
379+
metadata = {
380+
'sam': 1,
381+
'i': 2.1,
382+
'am': {
383+
'rhyme': [89]
384+
},
385+
}
386+
output = uworker_msg_pb2.Output(issue_metadata=json.dumps(metadata))
387+
wire_format = uworker_io.serialize_uworker_input(output)
388+
deserialized = uworker_io.deserialize_uworker_output(wire_format)
389+
self.assertEqual(json.loads(deserialized.issue_metadata), metadata)

0 commit comments

Comments
 (0)