1 /*
2 * Copyright (C) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "dumper.h"
17 #include <string>
18 #include <unistd.h>
19 #include <cstdio>
20 #include <mutex>
21 #include <sys/time.h>
22 #include <securec.h>
23 #include <unordered_map>
24 #include "directory_ex.h"
25 #include "media_errors.h"
26 #include "media_log.h"
27 #include "param_wrapper.h"
28
29 namespace {
30 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN, "GstDumper"};
31 static std::mutex g_padBufCountMutex;
32 static std::unordered_map<GstPad *, uint64_t> g_padBufCount = {};
33 }
34
35 namespace OHOS {
36 namespace Media {
DumpDotGraph(GstPipeline & pipeline,int32_t oldState,int32_t newState)37 void Dumper::DumpDotGraph(GstPipeline &pipeline, int32_t oldState, int32_t newState)
38 {
39 bool checkRet = (oldState < GST_STATE_VOID_PENDING) || (oldState > GST_STATE_PLAYING) ||
40 (newState < GST_STATE_VOID_PENDING) || (newState > GST_STATE_PLAYING);
41 CHECK_AND_RETURN_LOG(!checkRet, "invalid state, oldState: %{public}d, newState: %{public}d",
42 oldState, newState);
43
44 std::string dumpDir;
45 int res = OHOS::system::GetStringParameter("sys.media.dump.dot.path", dumpDir, "");
46 if (res != 0 || dumpDir.empty()) {
47 return;
48 }
49
50 std::string realPath;
51 CHECK_AND_RETURN_LOG(PathToRealPath(dumpDir, realPath), "invalid dump path: %{public}s", dumpDir.c_str());
52
53 struct timeval tv;
54 int ret = gettimeofday(&tv, nullptr);
55 CHECK_AND_RETURN_LOG(ret >= 0, "get time of day failed");
56
57 constexpr int32_t secPerHour = 60 * 60;
58 constexpr int32_t minutePerHour = 60;
59 constexpr int32_t secPerMinute = 60;
60 constexpr int32_t usecPerMSec = 1000;
61
62 uint64_t hour = static_cast<uint64_t>(tv.tv_sec / secPerHour);
63 uint32_t minute = static_cast<uint32_t>((tv.tv_sec / secPerMinute) % minutePerHour);
64 uint32_t sec = static_cast<uint32_t>(tv.tv_sec % minutePerHour);
65 uint32_t millsec = static_cast<uint32_t>(tv.tv_usec / usecPerMSec);
66
67 const gchar *oldName = gst_element_state_get_name(static_cast<GstState>(oldState));
68 const gchar *newName = gst_element_state_get_name(static_cast<GstState>(newState));
69
70 char fullPath[PATH_MAX] = { 0 };
71 const char *format = "%s/%" PRIu64 ".%u.%u.%u-media-pipeline.0x%06" PRIXPTR ".%s_%s.dot";
72 ret = sprintf_s(fullPath, PATH_MAX, format, realPath.c_str(), hour, minute, sec, millsec,
73 FAKE_POINTER(&pipeline), oldName, newName);
74 CHECK_AND_RETURN_LOG(ret > 0, "dump dot failed for 0x%{public}06" PRIXPTR " %{public}s to %{public}s",
75 FAKE_POINTER(&pipeline), oldName, newName);
76
77 FILE *fp = fopen(fullPath, "wb");
78 CHECK_AND_RETURN_LOG(fp != nullptr, "open path failed, %{public}s", fullPath);
79
80 gchar *buf = gst_debug_bin_to_dot_data(GST_BIN(&pipeline), GST_DEBUG_GRAPH_SHOW_ALL);
81 if (buf != nullptr) {
82 (void)fputs(buf, fp);
83 g_free(buf);
84 }
85
86 (void)fclose(fp);
87 MEDIA_LOGD("wrote pipeline graph to : '%{public}s'", fullPath);
88 }
89
DumpGstBuffer(GstPad * pad,GstPadProbeInfo * info,gpointer user_data)90 GstPadProbeReturn Dumper::DumpGstBuffer(GstPad *pad, GstPadProbeInfo *info, gpointer user_data)
91 {
92 (void)user_data;
93 CHECK_AND_RETURN_RET(pad != nullptr && info != nullptr, GST_PAD_PROBE_OK);
94
95 if ((GST_PAD_PROBE_INFO_TYPE(info) & GST_PAD_PROBE_TYPE_BUFFER) == 0) {
96 return GST_PAD_PROBE_OK;
97 }
98
99 GstBuffer *buf = gst_pad_probe_info_get_buffer(info);
100 CHECK_AND_RETURN_RET(buf != nullptr, GST_PAD_PROBE_OK);
101 GstMapInfo mapInfo = GST_MAP_INFO_INIT;
102 CHECK_AND_RETURN_RET(gst_buffer_map(buf, &mapInfo, GST_MAP_READ), GST_PAD_PROBE_OK);
103
104 uint64_t bufSeq = 0;
105 {
106 std::lock_guard<std::mutex> lock(g_padBufCountMutex);
107 if (g_padBufCount.count(pad) == 0) {
108 g_padBufCount.emplace(pad, 0);
109 }
110 bufSeq = g_padBufCount[pad]++;
111 }
112
113 char fullPath[PATH_MAX] = { 0 };
114 const char *format = "/data/media/dump/pad_%s_%s_buf_%" PRIu64 "";
115 if (sprintf_s(fullPath, PATH_MAX, format, GST_DEBUG_PAD_NAME(pad), bufSeq) <= 0) {
116 MEDIA_LOGE("dump buffer failed for 0x%{public}06" PRIXPTR ", pad is %{public}s:%{public}s",
117 FAKE_POINTER(buf), GST_DEBUG_PAD_NAME(pad));
118 gst_buffer_unmap(buf, &mapInfo);
119 return GST_PAD_PROBE_OK;
120 }
121
122 FILE *fp = fopen(fullPath, "wb");
123 if (fp != nullptr) {
124 (void)fwrite(mapInfo.data, mapInfo.size, 1, fp);
125 (void)fflush(fp);
126 (void)fclose(fp);
127 fp = nullptr;
128 }
129
130 gst_buffer_unmap(buf, &mapInfo);
131
132 MEDIA_LOGD("wrote buffer to %{public}s", fullPath);
133 return GST_PAD_PROBE_OK;
134 }
135
AddDumpGstBufferProbe(GstElement * element,const gchar * padname)136 void Dumper::AddDumpGstBufferProbe(GstElement *element, const gchar *padname)
137 {
138 GstPad *pad = gst_element_get_static_pad(element, padname);
139 CHECK_AND_RETURN(pad != nullptr);
140
141 (void)gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER, &Dumper::DumpGstBuffer, nullptr, nullptr);
142 gst_object_unref(pad);
143 }
144
IsEnableDumpGstBuffer()145 bool Dumper::IsEnableDumpGstBuffer()
146 {
147 int value = OHOS::system::GetIntParameter("sys.media.dump.gstbuffer", 0);
148 return value == 0 ? false : true;
149 }
150 } // namespace Media
151 } // namespace OHOS
152